Note
Click here to download the full example code or to run this example in your browser via Binder
01. Read BIDS datasets¶
When working with electrophysiological data in the BIDS format, an important resource is the OpenNeuro database. OpenNeuro works great with MNE-BIDS because every dataset must pass a validator that tests to ensure its format meets BIDS specifications before the dataset can be uploaded, so you know the data will work with a script like in this example without modification.
We have various data types that can be loaded via the read_raw_bids
function:
MEG
EEG (scalp electrodes)
iEEG (ECoG and SEEG)
the anatomical MRI scan of a study participant
In this tutorial, we show how read_raw_bids
can be used to load and
inspect BIDS-formatted data.
# Authors: Adam Li <adam2392@gmail.com>
# Richard Höchenberger <richard.hoechenberger@gmail.com>
# Alex Rockhill <aprockhill@mailbox.org>
#
# License: BSD (3-clause)
Imports¶
We are importing everything we need for this example:
import os
import os.path as op
import openneuro
from mne.datasets import sample
from mne_bids import BIDSPath, read_raw_bids, print_dir_tree, make_report
Download a subject’s data from an OpenNeuro BIDS dataset¶
Download the data, storing each in a target_dir
target directory, which,
in mne-bids
terminology, is the root of each BIDS dataset. This example
uses this EEG dataset of
resting-state recordings of patients with Parkinson’s disease.
# .. note: If the keyword argument include is left out of
# ``openneuro.download``, the whole dataset will be downloaded.
# We're just using data from one subject to reduce the time
# it takes to run the example.
dataset = 'ds002778'
subject = 'pd6'
# Download one subject's data from each dataset
bids_root = op.join(op.dirname(sample.data_path()), dataset)
if not op.isdir(bids_root):
os.makedirs(bids_root)
openneuro.download(dataset=dataset, target_dir=bids_root,
include=[f'sub-{subject}'])
Out:
Hello! This is openneuro-py 2021.8. Great to see you!
Please report problems and bugs at
https://github.com/hoechenberger/openneuro-py/issues
Preparing to download ds002778 ...
Retrieving up to 19 files (5 concurrent downloads).
CHANGES: 0%| | 0.00/269 [00:00<?, ?B/s]
README: 0%| | 0.00/4.38k [00:00<?, ?B/s]
dataset_description.json: 0%| | 0.00/350 [00:00<?, ?B/s]
participants.tsv: 0%| | 0.00/1.62k [00:00<?, ?B/s][A
participants.json: 0%| | 0.00/1.24k [00:00<?, ?B/s][A[A
[A
[A[A
sub-pd6_ses-on_task-rest_beh.tsv: 0.00B [00:00, ?B/s]
sub-pd6_ses-off_scans.tsv: 0%| | 0.00/75.0 [00:00<?, ?B/s]
sub-pd6_ses-on_task-rest_beh.json: 0%| | 0.00/433 [00:00<?, ?B/s]
sub-pd6_ses-on_task-rest_eeg.json: 0%| | 0.00/471 [00:00<?, ?B/s]
sub-pd6_ses-on_task-rest_channels.tsv: 0%| | 0.00/2.22k [00:00<?, ?B/s]
sub-pd6_ses-off_task-rest_beh.tsv: 0.00B [00:00, ?B/s]
sub-pd6_ses-off_task-rest_eeg.json: 0.00B [00:00, ?B/s]
sub-pd6_ses-on_task-rest_events.tsv: 0%| | 0.00/51.0 [00:00<?, ?B/s]
sub-pd6_ses-on_scans.tsv: 0%| | 0.00/74.0 [00:00<?, ?B/s]
sub-pd6_ses-off_task-rest_beh.json: 0%| | 0.00/436 [00:00<?, ?B/s]
sub-pd6_ses-on_task-rest_eeg.bdf: 0%| | 0.00/17.4M [00:00<?, ?B/s]
sub-pd6_ses-off_task-rest_channels.tsv: 0.00B [00:00, ?B/s][A
[A
sub-pd6_ses-on_task-rest_eeg.bdf: 0%| | 16.0k/17.4M [00:00<02:06, 144kB/s]
sub-pd6_ses-off_task-rest_events.tsv: 0%| | 0.00/66.0 [00:00<?, ?B/s][A
[A
sub-pd6_ses-off_task-rest_eeg.bdf: 0%| | 0.00/11.5M [00:00<?, ?B/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 0%| | 49.6k/17.4M [00:00<01:16, 236kB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 0%| | 19.6k/11.5M [00:00<01:04, 186kB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 1%| | 118k/17.4M [00:00<00:45, 398kB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 0%| | 53.6k/11.5M [00:00<00:46, 259kB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 1%|1 | 220k/17.4M [00:00<00:30, 597kB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 1%|1 | 122k/11.5M [00:00<00:28, 418kB/s] [A
sub-pd6_ses-on_task-rest_eeg.bdf: 3%|2 | 458k/17.4M [00:00<00:15, 1.13MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 2%|2 | 258k/11.5M [00:00<00:15, 743kB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 5%|5 | 934k/17.4M [00:00<00:07, 2.17MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 5%|4 | 530k/11.5M [00:00<00:08, 1.35MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 10%|9 | 1.66M/17.4M [00:00<00:04, 3.48MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 9%|8 | 1.03M/11.5M [00:00<00:04, 2.46MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 14%|#3 | 2.41M/17.4M [00:00<00:03, 4.26MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 15%|#4 | 1.70M/11.5M [00:00<00:02, 3.78MB/s][A
sub-pd6_ses-off_task-rest_eeg.bdf: 21%|##1 | 2.46M/11.5M [00:00<00:01, 5.04MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 18%|#8 | 3.20M/17.4M [00:01<00:03, 4.74MB/s]
sub-pd6_ses-on_task-rest_eeg.bdf: 23%|##2 | 3.95M/17.4M [00:01<00:02, 5.50MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 26%|##5 | 2.96M/11.5M [00:01<00:01, 4.67MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 26%|##5 | 4.49M/17.4M [00:01<00:03, 4.14MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 30%|##9 | 3.43M/11.5M [00:01<00:03, 2.63MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 28%|##8 | 4.95M/17.4M [00:01<00:03, 3.38MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 33%|###3 | 3.79M/11.5M [00:01<00:02, 2.71MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 32%|###2 | 5.57M/17.4M [00:01<00:03, 3.98MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 36%|###6 | 4.14M/11.5M [00:01<00:03, 2.48MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 35%|###4 | 6.02M/17.4M [00:02<00:04, 2.54MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 39%|###8 | 4.43M/11.5M [00:01<00:03, 1.99MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 38%|###8 | 6.68M/17.4M [00:02<00:03, 3.24MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 45%|####4 | 5.12M/11.5M [00:02<00:02, 2.88MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 41%|#### | 7.12M/17.4M [00:02<00:03, 2.97MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 50%|##### | 5.78M/11.5M [00:02<00:01, 3.25MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 43%|####3 | 7.49M/17.4M [00:02<00:04, 2.30MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 54%|#####3 | 6.15M/11.5M [00:02<00:02, 2.19MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 45%|####4 | 7.81M/17.4M [00:02<00:04, 2.42MB/s]
sub-pd6_ses-on_task-rest_eeg.bdf: 48%|####7 | 8.31M/17.4M [00:02<00:03, 2.94MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 56%|#####6 | 6.45M/11.5M [00:02<00:02, 1.98MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 50%|####9 | 8.66M/17.4M [00:03<00:02, 3.09MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 58%|#####8 | 6.70M/11.5M [00:03<00:02, 1.85MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 52%|#####1 | 9.02M/17.4M [00:03<00:03, 2.79MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 60%|###### | 6.92M/11.5M [00:03<00:02, 1.88MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 54%|#####4 | 9.38M/17.4M [00:03<00:02, 2.91MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 63%|######2 | 7.20M/11.5M [00:03<00:02, 2.00MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 56%|#####6 | 9.79M/17.4M [00:03<00:02, 3.10MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 65%|######5 | 7.46M/11.5M [00:03<00:01, 2.11MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 59%|#####8 | 10.2M/17.4M [00:03<00:02, 3.19MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 67%|######7 | 7.74M/11.5M [00:03<00:01, 2.17MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 61%|######1 | 10.6M/17.4M [00:03<00:02, 2.96MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 70%|######9 | 8.03M/11.5M [00:03<00:01, 2.13MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 63%|######3 | 11.0M/17.4M [00:03<00:02, 3.13MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 72%|#######2 | 8.31M/11.5M [00:03<00:01, 2.25MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 66%|######5 | 11.4M/17.4M [00:04<00:01, 3.23MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 75%|#######4 | 8.60M/11.5M [00:03<00:01, 2.23MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 68%|######8 | 11.9M/17.4M [00:04<00:01, 3.02MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 77%|#######7 | 8.87M/11.5M [00:03<00:01, 2.36MB/s][A
sub-pd6_ses-off_task-rest_eeg.bdf: 79%|#######9 | 9.10M/11.5M [00:04<00:01, 2.38MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 71%|####### | 12.3M/17.4M [00:04<00:01, 3.19MB/s]
sub-pd6_ses-on_task-rest_eeg.bdf: 73%|#######2 | 12.6M/17.4M [00:04<00:01, 3.02MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 81%|########1 | 9.34M/11.5M [00:04<00:01, 2.10MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 74%|#######4 | 12.9M/17.4M [00:04<00:01, 2.78MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 83%|########3 | 9.56M/11.5M [00:04<00:01, 1.72MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 77%|#######6 | 13.3M/17.4M [00:04<00:01, 2.98MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 85%|########5 | 9.78M/11.5M [00:04<00:01, 1.78MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 78%|#######8 | 13.6M/17.4M [00:04<00:01, 2.95MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 88%|########7 | 10.1M/11.5M [00:04<00:00, 1.92MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 80%|######## | 13.9M/17.4M [00:04<00:01, 3.00MB/s]
sub-pd6_ses-on_task-rest_eeg.bdf: 82%|########2 | 14.3M/17.4M [00:05<00:01, 3.14MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 90%|######### | 10.4M/11.5M [00:04<00:00, 2.14MB/s][A
sub-pd6_ses-off_task-rest_eeg.bdf: 93%|#########2| 10.6M/11.5M [00:04<00:00, 2.24MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 84%|########4 | 14.6M/17.4M [00:05<00:00, 3.00MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 95%|#########4| 10.9M/11.5M [00:05<00:00, 2.37MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 86%|########6 | 15.0M/17.4M [00:05<00:00, 3.15MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 97%|#########6| 11.1M/11.5M [00:05<00:00, 2.34MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 89%|########8 | 15.4M/17.4M [00:05<00:00, 3.52MB/s]
sub-pd6_ses-off_task-rest_eeg.bdf: 100%|#########9| 11.4M/11.5M [00:05<00:00, 2.53MB/s][A
sub-pd6_ses-on_task-rest_eeg.bdf: 91%|#########1| 15.9M/17.4M [00:05<00:00, 3.67MB/s]
[A
sub-pd6_ses-on_task-rest_eeg.bdf: 94%|#########3| 16.3M/17.4M [00:05<00:00, 3.92MB/s]
sub-pd6_ses-on_task-rest_eeg.bdf: 96%|#########6| 16.7M/17.4M [00:05<00:00, 3.86MB/s]
sub-pd6_ses-on_task-rest_eeg.bdf: 98%|#########8| 17.1M/17.4M [00:05<00:00, 3.67MB/s]
Finished downloading ds002778.
Please enjoy your brains.
Explore the dataset contents¶
We can use MNE-BIDS to print a tree of all
included files and folders. We pass the max_depth
parameter to
mne_bids.print_dir_tree
to the output to four levels of folders, for
better readability in this example.
print_dir_tree(bids_root, max_depth=4)
Out:
|ds002778/
|--- CHANGES
|--- README
|--- dataset_description.json
|--- participants.json
|--- participants.tsv
|--- sub-pd6/
|------ ses-off/
|--------- sub-pd6_ses-off_scans.tsv
|--------- beh/
|------------ sub-pd6_ses-off_task-rest_beh.json
|------------ sub-pd6_ses-off_task-rest_beh.tsv
|--------- eeg/
|------------ sub-pd6_ses-off_task-rest_channels.tsv
|------------ sub-pd6_ses-off_task-rest_eeg.bdf
|------------ sub-pd6_ses-off_task-rest_eeg.json
|------------ sub-pd6_ses-off_task-rest_events.tsv
|------ ses-on/
|--------- sub-pd6_ses-on_scans.tsv
|--------- beh/
|------------ sub-pd6_ses-on_task-rest_beh.json
|------------ sub-pd6_ses-on_task-rest_beh.tsv
|--------- eeg/
|------------ sub-pd6_ses-on_task-rest_channels.tsv
|------------ sub-pd6_ses-on_task-rest_eeg.bdf
|------------ sub-pd6_ses-on_task-rest_eeg.json
|------------ sub-pd6_ses-on_task-rest_events.tsv
We can even ask MNE-BIDS to produce a human-readbale summary report on the dataset contents.
print(make_report(bids_root))
Out:
Summarizing participants.tsv /home/stefanappelhoff/mne_data/ds002778/participants.tsv...
Summarizing scans.tsv files [PosixPath('/home/stefanappelhoff/mne_data/ds002778/sub-pd6/ses-on/sub-pd6_ses-on_scans.tsv'), PosixPath('/home/stefanappelhoff/mne_data/ds002778/sub-pd6/ses-off/sub-pd6_ses-off_scans.tsv')]...
The participant template found: comprised of 14 male and 17 female participants;
comprised of 31 right hand, 0 left hand and 0 ambidextrous; ages ranged from 47.0 to 82.0 (mean = 63.39, std = 8.69)
The UC San Diego Resting State EEG Data from Patients with Parkinson's Disease
dataset was created by Alexander P. Rockhill, Nicko Jackson, Jobi George, Adam
Aron, and Nicole C. Swann and conforms to BIDS version 1.2.2. This report was
generated with MNE-BIDS (https://doi.org/10.21105/joss.01896). The dataset
consists of 1 participants (comprised of 14 male and 17 female participants;
comprised of 31 right hand, 0 left hand and 0 ambidextrous; ages ranged from
47.0 to 82.0 (mean = 63.39, std = 8.69)) and 2 recording sessions: off, and on.
Data was recorded using a EEG system (Biosemi manufacturer) sampled at 512.0 Hz
with line noise at 50, and 60 Hz. There were 2 scans in total. Recording
durations ranged from 191.0 to 289.0 seconds (mean = 240.0, std = 49.0), for a
total of 480.0 seconds of data recorded over all scans. For each dataset, there
were on average 41.0 (std = 0.0) recording channels per scan, out of which 41.0
(std = 0.0) were used in analysis (0.0 +/- 0.0 were removed from analysis).
Now it’s time to get ready for reading some of the data! First, we need to
create an mne_bids.BIDSPath
, which is the workhorse object of
MNE-BIDS when it comes to file and folder operations.
For now, we’re interested only in the EEG data in the BIDS root directory of the Parkinson’s disease patient dataset. There were two sessions, one where the patients took their regular anti-Parkinsonian medications and one where they abstained for more than twelve hours. Let’s start with the off-medication session.
We can now retrieve a list of all MEG-related files in the dataset:
print(bids_path.match())
Out:
[BIDSPath(
root: /home/stefanappelhoff/mne_data/ds002778
datatype: eeg
basename: sub-pd6_ses-off_task-rest_channels.tsv), BIDSPath(
root: /home/stefanappelhoff/mne_data/ds002778
datatype: eeg
basename: sub-pd6_ses-off_task-rest_eeg.bdf), BIDSPath(
root: /home/stefanappelhoff/mne_data/ds002778
datatype: eeg
basename: sub-pd6_ses-off_task-rest_events.tsv)]
The returned list contains BIDSpaths
of 3 files:
sub-pd6_ses-off_task-rest_channels.tsv
,
sub-pd6_ses-off_task-rest_events.tsv
, and
sub-pd6_ses-off_task-rest_eeg.bdf
.
The first two are so-called sidecar files that contain information on the
recording channels and experimental events, and the third one is the actual
data file.
Prepare reading the data¶
There is only one subject and one experimental task (rest
).
Let’s use this knowledge to create a new BIDSPath
with
all the information required to actually read the EEG data. We also need to
pass a suffix
, which is the last part of the filename just before the
extension – 'channels'
and 'events'
for the two TSV files in
our example, and 'eeg'
for EEG raw data. For MEG and EEG raw data, the
suffix is identical to the datatype, so don’t let yourself be confused here!
Now let’s print the contents of bids_path
.
print(bids_path)
Out:
/home/stefanappelhoff/mne_data/ds002778/sub-pd6/ses-off/eeg/sub-pd6_ses-off_task-rest_eeg.bdf
You probably noticed two things: Firstly, this looks like an ordinary string now, not like the more-or-less neatly formatted output we saw before. And secondly, that there’s suddenly a filename extension which we never specified anywhere!
The reason is that when you call print(bids_path)
, BIDSPath
returns
a string representation of BIDSPath.fpath
, which looks different. If,
instead, you simply typed bids_path
(or print(repr(bids_path))
, which
is the same) into your Python console, you would get the nicely formatted
output:
Out:
BIDSPath(
root: /home/stefanappelhoff/mne_data/ds002778
datatype: eeg
basename: sub-pd6_ses-off_task-rest_eeg)
The root
here is – you guessed it – the directory we passed via the
root
parameter: the “home” of our BIDS dataset. The datatype
, again,
is self-explanatory. The basename
, on the other hand, is created
automatically based on the suffix and BIDS entities we passed to
BIDSPath
: in our case, subject
, session
and task
.
Note
There are many more supported entities, the most-commonly used among them
probably being acquisition
. Please see
our introduction to BIDSPath to learn more
about entities, basename
, and BIDSPath
in general.
But what about that filename extension, now? BIDSPath.fpath
, which –
as you hopefully remember – is invoked when you run print(bids_path)
–
employs some heuristics to auto-detect some missing filename components.
Omitting the filename extension in your script can make your code
more portable. Note that, however, you can explicitly specify an
extension too, by passing e.g. extension='.bdf'
to BIDSPath
.
Read the data¶
Let’s read the data! It’s just a single line of code.
raw = read_raw_bids(bids_path=bids_path, verbose=False)
Out:
Extracting EDF parameters from /home/stefanappelhoff/mne_data/ds002778/sub-pd6/ses-off/eeg/sub-pd6_ses-off_task-rest_eeg.bdf...
BDF file detected
Setting channel info structure...
Creating raw.info structure...
Reading events from /home/stefanappelhoff/mne_data/ds002778/sub-pd6/ses-off/eeg/sub-pd6_ses-off_task-rest_events.tsv.
Reading channel info from /home/stefanappelhoff/mne_data/ds002778/sub-pd6/ses-off/eeg/sub-pd6_ses-off_task-rest_channels.tsv.
Now we can inspect the raw
object to check that it contains to correct
metadata.
Basic subject metadata is here.
print(raw.info['subject_info'])
Out:
{'his_id': 'sub-pd6', 'age': '62', 'gender': 'f', 'hand': 1, 'MMSE': '30', 'NAART': '42', 'disease_duration': '8', 'rl_deficits': 'L OFF meds, more R ON meds', 'notes': 'Used preprocessed data from EEGLAB .mat file instead of raw data for pd on'}
Power line frequency is here.
print(raw.info['line_freq'])
Out:
60
Sampling frequency is here.
print(raw.info['sfreq'])
Out:
512.0
Events are now Annotations
print(raw.annotations)
Out:
<Annotations | 2 segments: 1 (1), 65536 (1)>
Plot the raw data.
raw.plot()
Out:
<MNEBrowseFigure size 1402x756 with 4 Axes>
Total running time of the script: ( 0 minutes 12.355 seconds)