Importing

Initialize NeuroAnalyzer
using NeuroAnalyzer

Supported formats

import_recording() imports the following formats:

Format Extension(s) format Content detection Importer
EDF / EDF+ .edf :edf yes import_edf()
BDF / BDF+ .bdf :bdf yes import_bdf()
GDF 1.x / 2.x .gdf :gdf yes import_gdf()
BrainVision .vhdr, .ahdr :bv yes import_bv()
CSV .csv :csv text only import_csv()
CSV (gzip-compressed) .csv.gz :csv_gz gzip only import_csv()
EEGLAB dataset .set :set MAT-file only import_set()
NumPy array (e.g. from MNE) .npy :npy yes import_npy()
Extensible Data Format (LSL) .xdf :xdf yes import_xdf()
Neurodata Without Borders .nwb :nwb yes import_nwb()
Neuralynx CSC .ncs :ncs yes import_ncs()
MEGIN (Elekta Neuromag) FIFF .fif, .fiff :fiff yes import_fiff()
FieldTrip MATLAB .mat :ft MAT-file only import_ft()
SNIRF .snirf :snirf yes import_snirf()
NIRS (Homer) .nirs :nirs MAT-file only import_nirs()
NIRx (NIRStar / Aurora) .hdr :nirx yes import_nirx()
DuoMAG TMS MEP .ascii, .m :duomag no import_duomag()

“MAT-file only”, “text only” and “gzip only” mean that the container is verified, but the extension (or format) decides which importer is used.

Other formats are imported with dedicated functions only:

Format Importer Returns
Alice 4 (Philips Respironics) EDF import_alice4() NEURO object
DigiTrack (Elmiko) ASCII import_digitrack() NEURO object
Neuroscan continuous .cnt import_cnt() NEURO object
Thymatron ECT EEG printouts (scanned images) import_thymatron() NEURO object
EDF+ annotations-only files import_edf_annotations() markers (DataFrame)
Neuroscan Stim2 / Gentask behavioral .dat import_dat() trials (DataFrame)
FieldTrip events (.mat) import_ft(...; type = :events) events (DataFrame)
Channel locations import_locs() locations (DataFrame), see Channel locations
Montages import_montage() montage definition

import_recording()

import_recording() is a meta-function: it detects the file format and calls the appropriate import_*() function. Detection inspects the file contents (magic bytes, header signature) first and the extension second, so misnamed or extensionless files are handled where the format is identifiable; a warning is shown if the extension and the contents disagree. The data type (EEG, MEG, NIRS, …) is detected where possible and stored in obj.header.recording[:data_type].

eeg = import_recording("files/eeg/eeg.edf")
info(eeg)
              Data type: EEG
            File format: EDF
            Source file: files/eeg/eeg.edf
         File size [MB]: 14.13
       Memory size [MB]: 61.2
                Subject:  528004  SIT 52, 20220831-122227-{d589f756-53fc-4f1b-915d-6e3b8c1560ad}
              Recording: EEGDigiTrack EEG-1042 (42-channel EEG Amplifier) V0.5 Rev. 41
        Recording notes: 
         Recording date: 31.08.22
         Recording time: 12:32:53
     Sampling rate (Hz): 256
Signal length [samples]: 308480
Signal length [seconds]: 1205.0
     Number of channels: 24
              Epochs ID: 
       Number of epochs: 1
 Epoch length [samples]: 308480
 Epoch length [seconds]: 1205.0
         Reference type: physical
         Line frequency: 50 Hz
                Markers: no
      Channel locations: yes
 Number of EEG channels: 19

Channels:
 ch     label           type        unit    bad     
 ------ --------------- ----------- ------- -------
 1      Fp1             EEG         μV      false   
 2      Fp2             EEG         μV      false   
 3      F3              EEG         μV      false   
 4      F4              EEG         μV      false   
 5      C3              EEG         μV      false   
 6      C4              EEG         μV      false   
 7      P3              EEG         μV      false   
 8      P4              EEG         μV      false   
 9      O1              EEG         μV      false   
 10     O2              EEG         μV      false   
 11     F7              EEG         μV      false   
 12     F8              EEG         μV      false   
 13     T3              EEG         μV      false   
 14     T4              EEG         μV      false   
 15     T5              EEG         μV      false   
 16     T6              EEG         μV      false   
 17     A1              REF         μV      false   
 18     A2              REF         μV      false   
 19     Fz              EEG         μV      false   
 20     Cz              EEG         μV      false   
 21     Pz              EEG         μV      false   
 22     EOG1            EOG         μV      false   
 23     EOG2            EOG         μV      false   
 24     ECG             ECG         μV      false   

detect_format() shows which format would be used:

for f in ["files/eeg/eeg.edf",
          "files/eeg/eeg-test-bv.vhdr",
          "files/meg/meg-test-fiff.fif",
          "files/nirs/fnirs-test-snirf.snirf"]
    println(rpad(f, 38), detect_format(f))
end
files/eeg/eeg.edf                     edf
files/eeg/eeg-test-bv.vhdr            bv
files/meg/meg-test-fiff.fif           fiff
files/nirs/fnirs-test-snirf.snirf     snirf

The format can also be set explicitly, skipping detection, e.g. import_recording("recording.dat"; format = :edf).

Some formats need additional information:

  • .npy files contain only the signal: the sampling rate must be given, import_recording(file; sampling_rate = 256)
  • FieldTrip .mat files may contain different data types: import_recording(file; type = :eeg) (:eeg, :ecog, :ieeg, :seeg, :meg, :nirs or :events)
  • SNIRF files may contain several subjects: n selects the subject

All test recordings imported with import_recording():

test_files = [
    ("files/eeg/eeg-test-edf.edf", (;)),
    ("files/eeg/eeg-test-edfplus.edf", (;)),
    ("files/eeg/eeg-test-bdf.bdf", (;)),
    ("files/eeg/eeg-test-gdf_2.20.gdf", (;)),
    ("files/eeg/eeg-test-bv.vhdr", (;)),
    ("files/eeg/eeg-test_txch.csv.gz", (;)),
    ("files/eeg/eeg-test-eeglab.set", (;)),
    ("files/eeg/eeg-test-npy.npy", (sampling_rate = 256,)),
    ("files/eeg/eeg-test-xdf.xdf", (;)),
    ("files/eeg/eeg-test-ncs.ncs", (;)),
    ("files/eeg/eeg-test-fieldtrip.mat", (type = :eeg,)),
    ("files/meg/meg-test-fiff.fif", (;)),
    ("files/nirs/fnirs-test-snirf.snirf", (;)),
    ("files/nirs/fnirs-test-nirs.nirs", (;)),
    ("files/nirx/NIRS-2020-08-18_001.hdr", (;)),
    ("files/mep/mep-duomag.ascii", (;)),
]
NeuroAnalyzer.verbose = false
for (f, kw) in test_files
    obj = import_recording(f; kw...)
    println(rpad(basename(f), 30),
            rpad(":" * string(detect_format(f)), 9),
            rpad(datatype(obj), 6),
            lpad(nchannels(obj), 4), " channels, ",
            lpad(sr(obj), 5), " Hz")
end
NeuroAnalyzer.verbose = true;
eeg-test-edf.edf              :edf     eeg     24 channels,   256 Hz
eeg-test-edfplus.edf          :edf     eeg     29 channels,   200 Hz
eeg-test-bdf.bdf              :bdf     eeg     17 channels,  2048 Hz
eeg-test-gdf_2.20.gdf         :gdf     eeg     65 channels,  2048 Hz
eeg-test-bv.vhdr              :bv      eeg      2 channels,  1000 Hz
eeg-test_txch.csv.gz          :csv_gz  eeg     24 channels,   256 Hz
eeg-test-eeglab.set           :set     eeg     24 channels,   256 Hz
eeg-test-npy.npy              :npy     eeg     16 channels,   256 Hz
eeg-test-xdf.xdf              :xdf     eeg      5 channels,   256 Hz
eeg-test-ncs.ncs              :ncs     ieeg     1 channels, 32051 Hz
eeg-test-fieldtrip.mat        :ft      eeg     24 channels,   256 Hz
meg-test-fiff.fif             :fiff    meg    327 channels,  1000 Hz
fnirs-test-snirf.snirf        :snirf   nirs    72 channels,    50 Hz
fnirs-test-nirs.nirs          :nirs    nirs    12 channels,    25 Hz
NIRS-2020-08-18_001.hdr       :nirx    nirs    26 channels,    25 Hz
mep-duomag.ascii              :duomag  mep     19 channels, 12500 Hz

Channel types

By default, channel types (EEG, EOG, ECG, reference, …) are recognized from the channel labels. For recordings with non-standard labels this may fail; detection can be disabled with detect_type = false, in which case all signal channels get the data type of the recording:

eeg1 = import_recording("files/eeg/eeg.edf")
eeg2 = import_recording("files/eeg/eeg.edf"; detect_type = false)
for idx in 16:24
    println(rpad(labels(eeg1)[idx], 6),
            "detect_type = true: ", rpad(chtypes(eeg1)[idx], 5),
            "detect_type = false: ", chtypes(eeg2)[idx])
end
T6    detect_type = true: eeg  detect_type = false: eeg
A1    detect_type = true: ref  detect_type = false: eeg
A2    detect_type = true: ref  detect_type = false: eeg
Fz    detect_type = true: eeg  detect_type = false: eeg
Cz    detect_type = true: eeg  detect_type = false: eeg
Pz    detect_type = true: eeg  detect_type = false: eeg
EOG1  detect_type = true: eog  detect_type = false: eeg
EOG2  detect_type = true: eog  detect_type = false: eeg
ECG   detect_type = true: ecg  detect_type = false: eeg

Channel types can be changed later with set_channel_type!().

Channel labels

Channel labels are normalized on import (clean_labels = true, EEG and MEG formats), e.g.:

  • format prefixes are removed: "EEG Fp1" → "Fp1"
  • reference suffixes are removed: "FP1-REF", "C3-LE" → "Fp1", "C3"
  • 10-20 / 10-10 names get the standard capitalization: "FP1" → "Fp1", "CZ" → "Cz"
  • trailing padding dots are removed: "Fc5." → "FC5"
  • duplicated type prefixes are merged: "EOG EOG" → "EOG"
  • numbered MEG system channels get exactly one space before the number, keeping leading zeros: "MEG0113" → "MEG 0113"

Labels that would become identical after cleaning keep their original form. To keep the labels as stored in the file, use clean_labels = false:

eeg_raw = import_recording("files/eeg/eeg.edf"; clean_labels = false)
for idx in [1, 2, 19, 22, 24]
    println(rpad(labels(eeg_raw)[idx], 10), " → ", labels(eeg1)[idx])
end
EEG Fp1    → Fp1
EEG Fp2    → Fp2
EEG Fz     → Fz
EOG EOG1   → EOG1
ECG EKG    → ECG

Channel labels can be changed later with rename_channel!().

Sampling rate

Sampling rates are stored as integers. A non-integer sampling rate is converted on import: if \(k \cdot f_s\) is an integer for some \(k \le 16\), the data are upsampled exactly by \(k\) (e.g. NIRx 12.5 Hz → 25 Hz, keeping the original samples); otherwise they are resampled to the nearest integer rate. Rates within 0.01% of an integer (e.g. estimated from time stamps) are only rounded.

Format-Specific Notes

EDF / EDF+

eeg = import_edf("files/eeg/eeg-test-edf.edf")
eeg = import_edf("files/eeg/eeg-test-edfplus.edf")

EDF+ annotations are imported as markers. Annotations stored in a separate, annotations-only EDF+ file are imported with import_edf_annotations(), which returns a DataFrame with the same columns as obj.markers (it can be assigned, e.g. eeg.markers = annotations).

EDF files exported from the Alice 4 polysomnography system do not conform to the standard (the number of data records is not stored and channels may have different sampling rates); they are imported with import_alice4(), which upsamples all channels to the highest sampling rate:

eeg = import_alice4("recording.edf")

BDF / BDF+ and GDF

eeg = import_bdf("files/eeg/eeg-test-bdf.bdf")
eeg = import_gdf("files/eeg/eeg-test-gdf_1.25.gdf")
eeg = import_gdf("files/eeg/eeg-test-gdf_2.20.gdf")

BrainVision

A BrainVision recording consists of a header (.vhdr or .ahdr), a data file (.eeg) and an optional markers file (.vmrk). The header file is imported; the data and markers files are found using the names stored in the header and must be in the same folder:

eeg = import_bv("files/eeg/eeg-test-bv.vhdr")

CSV

The orientation is detected from the first column:

  • samples × channels: the first column contains time points (numbers) and the remaining columns the channels, with channel labels in the header row,
  • channels × samples: the first column contains channel labels and the header row the time points.

Time points must be in milliseconds; the sampling rate is calculated from them. Gzip-compressed files (.csv.gz) are read directly:

# channels × samples
eeg = import_csv("files/eeg/eeg-test_chxt.csv.gz")
# samples × channels
eeg = import_csv("files/eeg/eeg-test_txch.csv.gz")

EEGLAB

eeg = import_set("files/eeg/eeg-test-eeglab.set")

Epoched datasets and events are imported; data stored in a separate .fdt file are read from the same folder.

Neuroscan and DigiTrack

Neuroscan .cnt files do not store the sample width; it must be given (:i16 or :i32) according to the recording settings:

eeg = import_cnt("files/eeg/eeg-test-cnt.cnt"; data_format = :i32)
eeg = import_digitrack("files/eeg/eeg-test-digitrack.txt")

Behavioral data from Neuroscan Stim2 / Gentask (.dat) are imported as a DataFrame with one row per trial:

trials = import_dat("files/eeg/eeg-test-dat.dat")
first(trials, 5)
5×5 DataFrame
Row trial response type correct latency
Int64 Int64 Int64 Int64 Int64
1 2 0 4 1 1600
2 3 0 4 1 1600
3 4 0 1 1 1600
4 5 2 2 1 282
5 6 0 4 1 1600

MEG (FIFF)

meg = import_fiff("files/meg/meg-test-fiff.fif")

Recordings made with internal active shielding (MaxShield) are refused unless allow_maxshield = true; see Introduction to MEG.

NIRS

nirs = import_snirf("files/nirs/fnirs-test-snirf.snirf")
nirs = import_nirs("files/nirs/fnirs-test-nirs.nirs")
nirs = import_nirx("files/nirx/NIRS-2020-08-18_001.hdr")

For NIRx recordings, the header file (.hdr) is imported; the other files of the recording (.wl1, .wl2, .evt, _probeInfo.mat, …) must be in the same folder.

FieldTrip

eeg = import_ft("files/eeg/eeg-test-fieldtrip.mat"; type = :eeg)
meg = import_ft("files/meg/meg-test-fieldtrip.mat"; type = :meg)
nirs = import_ft("files/nirs/fnirs-test-fieldtrip.mat"; type = :nirs)
events = import_ft("files/misc/events-test-fieldtrip.mat"; type = :events)

Other formats

# NumPy array: the sampling rate must be given
eeg = import_npy("files/eeg/eeg-test-npy.npy"; sampling_rate = 256)
# Extensible Data Format (Lab Streaming Layer)
eeg = import_xdf("files/eeg/eeg-test-xdf.xdf")
# Neuralynx continuous sampled channel
ieeg = import_ncs("files/eeg/eeg-test-ncs.ncs")
# Neurodata Without Borders
eeg = import_nwb("recording.nwb")
# DuoMAG TMS motor evoked potentials
mep = import_duomag("files/mep/mep-duomag.ascii")
# Thymatron ECT EEG printouts (scanned images, one channel per image)
eeg = import_thymatron(["files/thymatron/1.png",
                        "files/thymatron/2.png",
                        "files/thymatron/3.png"])

Exporting

export_csv() exports the signal to a CSV file (one column per channel, with time points in the first column). Header, markers, channel locations, epoch time points and processing history can be exported to additional files:

Keyword File
(always) <base>.csv
header=true <base>_header.txt
epoch_time=true <base>_epoch_time.csv
markers=true <base>_markers.csv
locs=true <base>_locs.csv
history=true <base>_history.txt

Existing files are overwritten only with overwrite = true.

Exporting the first 10 seconds of the recording, with channel locations:

load_locs!(eeg; file_name = "files/locs/standard-10-20-cap19-elmiko.locs")
# first 10-second epoch
eeg10 = keep_epoch(epoch(eeg; ep_len = 10); ep = 1)
out_dir = mktempdir()
export_csv(eeg10;
           file_name = joinpath(out_dir, "eeg.csv"),
           header = true,
           locs = true,
           history = true)
for f in readdir(out_dir)
    println(rpad(f, 20),
            round(filesize(joinpath(out_dir, f)) / 2^10; digits = 1), " kB")
end
eeg.csv             1140.0 kB
eeg_header.txt      2.4 kB
eeg_history.txt     0.6 kB
eeg_locs.csv        2.0 kB

Markers and channel locations can also be exported separately:

export_markers(eeg; file_name = "markers.csv")
export_locs(eeg; file_name = "locs.ced")

The extension of export_locs() sets the locations format (see Channel locations).

To use the signal in other Julia packages, it can be converted into a DataFrame:

df = to_df(eeg10)
df[1:5, 1:6]
5×6 DataFrame
Row time Fp1 Fp2 F3 F4 C3
Float64 Float64 Float64 Float64 Float64 Float64
1 0.0 741.646 1620.86 893.562 1247.97 900.378
2 0.00390625 923.694 1541.58 1269.85 1032.74 1349.13
3 0.0078125 1140.54 1318.64 1386.79 841.728 1531.18
4 0.0117188 1107.71 1246.9 1086.37 929.613 1195.06
5 0.015625 864.327 1421.77 767.115 1183.94 789.894

Saving and Loading

A NEURO object is saved with all its contents (signal, header, markers, channel locations and processing history) in a single file, in the HDF5-compatible JLD2 format. The file extension must be .hdf; existing files are overwritten only with overwrite = true:

file_name = joinpath(out_dir, "eeg.hdf")
save_neuro(eeg10; file_name = file_name)
eeg_loaded = load_neuro(file_name)
println("file size:        ",
        round(filesize(file_name) / 2^10; digits = 1), " kB")
println("identical signal: ", eeg_loaded.data == eeg10.data)
println("identical labels: ", labels(eeg_loaded) == labels(eeg10))
println("history:")
foreach(h -> println("  ", first(h, 70)), eeg_loaded.history)
file size:        548.8 kB
identical signal: true
identical labels: true
history:
  epoch(obj; marker=, offset=0, ep_len=2560)
  delete_epoch(obj; ep=[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 
  keep_epoch(obj; ep=1)

Saving is the fastest way to continue an analysis later: loading a saved object is much faster than importing and preprocessing the original recording again.