Initialize NeuroAnalyzer
using NeuroAnalyzerusing NeuroAnalyzerSupported 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() 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))
endfiles/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).mat files may contain different data types: import_recording(file; type = :eeg) (:eeg, :ecog, :ieeg, :seeg, :meg, :nirs or :events)n selects the subjectAll 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])
endT6 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.:
"EEG Fp1" → "Fp1""FP1-REF", "C3-LE" → "Fp1", "C3""FP1" → "Fp1", "CZ" → "Cz""Fc5." → "FC5""EOG EOG" → "EOG""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])
endEEG 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.
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:
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)| 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"])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")
endeeg.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]| 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 |
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.