-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MRG+1] Add bep006 eeg #78
Conversation
0cf73db
to
523a631
Compare
@sappelhoff I didn't look closely but PR looks clean so far. Could you keep the diff small so it's easy to review? Thanks! |
So the skeleton to include EEG is ready. Please make a first round of reviews @jasmainak @teonbrooks @monkeyman192 so that I know what you consider a total no-go or for what you have better ideas. What is still missing is
PS:
This is really hard, because MNE-BIDS is quite MEG-centric and several changes have to be made to generalize to the other modalities |
@sappelhoff what I'd really like to see even before examples is tests ... |
examples/make_eeg_bids.py
Outdated
# ---------- modality | ||
# ---------- sub-xx_task-yy_modality.ext | ||
# ---------- sub-xx_task-yy_modality.json | ||
# ---------- sub-xx_task-yy_events.tsv |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest removing L139--150. L153 will basically take care of it
examples/make_eeg_bids.py
Outdated
from mne.io import read_raw_brainvision | ||
|
||
from mne_bids import raw_to_bids | ||
from mne_bids.datasets import download_matchingpennies_subj |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the function is missing right now
Also, if I remember correctly, this dataset was already in BIDS format. How do you propose to use it in the example in that case?
Diff looks clean so far :) Tests next. |
examples/make_eeg_bids.py
Outdated
# | ||
# We are reading the data using MNE-Python's io module and the | ||
# `read_raw_brainvision` function, which takes a `.vhdr` file as an input. | ||
vhdr_path = os.path.join(data_dir, 'sub-05_task-matchingpennies_eeg.vhdr') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually thinking a bit more, why not add an example for the physionet
data: https://martinos.org/mne/dev/generated/mne.datasets.eegbci.load_data.html ? The fetcher already exists and there are many subjects (we could just fetch 2 or 3) -- so we can demonstrate how to do the conversion for more than n=1 subjects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an excellent idea, I'm on it!
wrt the extra stuff in the |
EEGLAB bug needs to be solved to continue: mne-tools/mne-python#5503 |
okay I see! Feel free to ping me if you need a review on the PR to fix the bug |
mne_bids/mne_bids.py
Outdated
@@ -253,7 +271,7 @@ def _scans_tsv(raw, raw_fname, fname, verbose): | |||
""" | |||
# get MEASurement date from the data info | |||
meas_date = raw.info['meas_date'] | |||
if isinstance(meas_date, (np.ndarray, list)): | |||
if isinstance(meas_date, (np.ndarray, list, tuple)): | |||
meas_date = meas_date[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In master of MNE-Python, meas_date
has just been formatted to consistently be a tuple. So soon, we will be able to get rid of np.ndarray and list over there. Should I make a comment so we don't forget? mne-tools/mne-python#5500
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make a comment and add an issue for the mne version that we will freeze for that particular release
examples/make_eeg_bids.py
Outdated
@@ -0,0 +1,199 @@ | |||
""" | |||
========================= | |||
Use MNE-Bids for EEG Data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Bids -> BIDS
examples/make_eeg_bids.py
Outdated
Use MNE-Bids for EEG Data | ||
========================= | ||
|
||
In this example, we use MNE-Bids to create a BIDS-compatible directory of EEG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here too
examples/make_eeg_bids.py
Outdated
# With this simple step we have everything to start a new BIDS directory using | ||
# our data. To do that, we can use the high level function `raw_to_bids`, which | ||
# is the core of MNE-BIDS. Generally, `raw_to_bids` tries to extract as much | ||
# meta data as possible from the raw data and then format it in a BIDS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
format -> formats
mne_bids/mne_bids.py
Outdated
@@ -253,7 +271,7 @@ def _scans_tsv(raw, raw_fname, fname, verbose): | |||
""" | |||
# get MEASurement date from the data info | |||
meas_date = raw.info['meas_date'] | |||
if isinstance(meas_date, (np.ndarray, list)): | |||
if isinstance(meas_date, (np.ndarray, list, tuple)): | |||
meas_date = meas_date[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make a comment and add an issue for the mne version that we will freeze for that particular release
# for FIF, we need to re-save the file to fix the file pointer | ||
# for files with multiple parts | ||
# Copy the imaging data files | ||
# Re-save FIF files to fix the file pointer for files with multiple parts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should a comment that this is not currently implemented. I am working on it with this pr: mne-tools/mne-python#5470
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@teonbrooks I guess we would need a copyfile_fif
function eventually? Or is your MNE PR going to take care of it?
mne_bids/tests/test_mne_bids.py
Outdated
|
||
def test_set(): | ||
"""Test raw_to_bids conversion for EEGLAB data.""" | ||
output_path = '/home/stefanappelhoff/Desktop/bogus' # _TempDir() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guessing this path is temp for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, some internal testing - thanks for noting it
mne_bids/tests/test_utils.py
Outdated
|
||
# IO error testing | ||
with pytest.raises(ValueError): | ||
copyfile_brainvision(raw_fname, new_name+'.eeg') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whitespace around operator? not sure if this is a pep8 violation or just style
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmmh I'd say it's a violation. Curiously, my linter did not catch it (linter-flake8 in atom)
mne_bids/tests/test_utils.py
Outdated
|
||
# IO error testing | ||
with pytest.raises(ValueError): | ||
copyfile_eeglab(raw_fname, new_name+'.wrong') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
# Make sure we are dealing with file names as is customary, not paths | ||
# Paths are problematic when copying the files to another system. Instead, | ||
# always use the file name and keep the file triplet in the same directory | ||
assert os.sep not in eeg_file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can these paths be in different folder hierarchies at recording? if so, maybe we should have a better error handling here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For both BrainVision and EEGLAB we can have arbitrary pointers to files. When these files are written by e.g., BrainVision Recorder of saved with EEGLAB the file pointers will always be simply the file name and we assume the file that is being pointed to to be in the same directory.
However, a user might manually change the pointers and include a path instead of only the file name. That is something I have not yet encountered ... but it is technically possible.
mne_bids/utils.py
Outdated
fname_dest, ext_dest = _parse_ext(vhdr_dest) | ||
if ext_src != ext_dest: | ||
raise ValueError('Need to move data with same extension' | ||
' but got {}, {}'.format(ext_src, ext_dest)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should include double quotes are {} to highlight str extensions
mne_bids/utils.py
Outdated
|
||
Notes | ||
----- | ||
NOT IMPLEMENTED YET. This function will abort upon the encounter of a .fdt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wip
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good so far. are you going to wait til the eeglab files are supported first?
thanks for the review @teonbrooks !
No, I wouldn't: If users run into a problem with that, they can reconvert their |
da9caf7
to
5ea68e4
Compare
for subj_idx in [1, 2]: | ||
eegbci.load_data(subject=subj_idx, | ||
runs=tasks, | ||
path=mne_dir, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah you did this. Okay, I can live with it. It's fine ...
Good to go from my end. @teonbrooks @choldgraf or @agramfort do you want to take a look before I merge? |
is it possible to see the rendered doc? |
yes - It's in the artifacts tab of the circle ci service: https://119-89170358-gh.circle-artifacts.com/0/html/auto_examples/make_eeg_bids.html |
examples/make_eeg_bids.py
Outdated
# initial `dataset_description` on top! | ||
# | ||
# Now it's time to manually check the BIDS directory and the meta files to add | ||
# all the information that MNE-BIDS could not infer. these places are marked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these -> These
|
||
# EDF (european data format) or BDF (biosemi) format | ||
elif ext == '.edf' or ext == '.bdf': | ||
raw = io.read_raw_edf(raw_fname, preload=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is using preload=True necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because MNE-Python wants it so for the EDF reader, else you get:
RuntimeError: EDF+ Annotations (TAL) channel needs to be parsed completely on loading. You must set preload parameter to True.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok fair enough.
I get we need to fix this in mne-python. We should not need to preload to get the raw.annotations available.
cc @massich
# MNE-Python's io module and the `read_raw_edf` function. | ||
data_dir = os.path.join(data_dir, 'physiobank', 'database', 'eegmmidb') | ||
edf_path = os.path.join(data_dir, 'S001', 'S001R02.edf') | ||
raw = read_raw_edf(edf_path, preload=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here. Why preload=True?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above, this is for all EDF files
alright, merged @sappelhoff ! Good way to start your week ;) |
@@ -15,6 +15,7 @@ Current | |||
Changelog | |||
~~~~~~~~~ | |||
|
|||
- Add support for EEG and a corresponding example: `make_eeg_bids.py` by `Stefan Appelhoff`_ (`#78 <https://github.com/mne-tools/mne-bids/pull/78>`_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, this should have been added at the bottom. Okay we can fix it later
we have EEG support! 🎉 thanks @sappelhoff! |
This will introduce BEP_006 "EEG" to MNE-BIDS and close #34
Current state of the BEP_006: Google doc
BEP_006 Examples: https://github.com/incf/bids-examples/tree/bep006_eeg
The bids-validator can already deal with EEG data by including the bep006 flag such as:
bids-validator --bep006 /path/to/dataset
To Do:
kind
coordsystem.json
andelectrodes.tsv
for EEG until the handling of coordinates is resolved in MNE-Python (i.e., no rescaling of the coordinates read from a file: WIP: Fix topomap plotting for EEG mne-python#5472)scans.tsv
is equal across modalities, write it for all (so far it was only written for meg)events.tsv
has been adjusted already in [MRG] bug fixes, test additions,meg_bids
-->mne_bids
... Paving the way for EEG inclusion #55, no changessidecar.json
to better cover all shared keys. Issue for EEG REQUIRED:EEGReference
... make itn/a
until we find a good way to infer it from mne.rawmne.find_events
, it wants a parameterinitialEvent=True
preload=True
General list of things to implement
Must-haves in the future:
coordsystem.json
for EEGelectrodes.tsv
for EEGNice to haves ... but not too important and hard to implement
... for these, I currently do not have an idea how to extract the information automatically. Adding them as optional (None-defaulting) parameters would clutter the API - so I don't work on them for now.
sidecar.json
fields in decreasing order of importance--> as a potential solution, see #47