Note
Click here to download the full example code
MOABB Dataset Example¶
In this example, we show how to fetch and prepare a MOABB dataset for usage with Braindecode.
# Authors: Lukas Gemein <l.gemein@gmail.com>
# Hubert Banville <hubert.jbanville@gmail.com>
# Simon Brandt <simonbrandt@protonmail.com>
#
# License: BSD (3-clause)
from collections import OrderedDict
import matplotlib.pyplot as plt
from IPython.display import display
from braindecode.datasets import MOABBDataset
from braindecode.datautil.windowers import \
create_windows_from_events, create_fixed_length_windows
from braindecode.datautil.preprocess import preprocess, MNEPreproc
First, we create a dataset based on BCIC IV 2a fetched with MOABB,
ds = MOABBDataset(dataset_name="BNCI2014001", subject_ids=[1])
Out:
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
ds has a pandas DataFrame with additional description of its internal datasets
display(ds.description)
Out:
subject ... run
0 1 ... run_0
1 1 ... run_1
2 1 ... run_2
3 1 ... run_3
4 1 ... run_4
5 1 ... run_5
6 1 ... run_0
7 1 ... run_1
8 1 ... run_2
9 1 ... run_3
10 1 ... run_4
11 1 ... run_5
[12 rows x 3 columns]
We can iterate through ds which yields one time point of a continuous signal x, and a target y (which can be None if targets are not defined for the entire continuous signal).
Out:
(26, 1) None
We can apply preprocessing transforms that are defined in mne and work in-place, such as resampling, bandpass filtering, or electrode selection.
transforms = [
MNEPreproc("pick_types", eeg=True, meg=False, stim=True),
MNEPreproc("resample", sfreq=100),
]
print(ds.datasets[0].raw.info["sfreq"])
preprocess(ds, transforms)
print(ds.datasets[0].raw.info["sfreq"])
Out:
250.0
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
48 events found
Event IDs: [1 2 3 4]
100.0
We can easily split ds based on a criteria applied to the description DataFrame:
Out:
{'session_E': 232164, 'session_T': 232164}
Next, we use a windower to extract events from the dataset based on events:
windows_ds = create_windows_from_events(
ds, trial_start_offset_samples=0, trial_stop_offset_samples=100,
window_size_samples=400, window_stride_samples=100,
drop_last_window=False)
Out:
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
96 matching events found
No baseline correction applied
0 projection items activated
Loading data for 96 events and 400 original time points ...
0 bad epochs dropped
We can iterate through the windows_ds which yields a window x, a target y, and window_ind (which itself contains i_window_in_trial, i_start_in_trial, and i_stop_in_trial, which are required for combining window predictions in the scorer).
for x, y, window_ind in windows_ds:
print(x.shape, y, window_ind)
break
Out:
Loading data for 1 events and 400 original time points ...
(23, 400) 3 [0, 300, 700]
We visually inspect the windows:

Out:
Loading data for 1 events and 400 original time points ...
Loading data for 1 events and 400 original time points ...
Loading data for 1 events and 400 original time points ...
Alternatively, we can create evenly spaced (“sliding”) windows using a different windower.
sliding_windows_ds = create_fixed_length_windows(
ds, start_offset_samples=0, stop_offset_samples=0,
window_size_samples=1200, window_stride_samples=1000,
drop_last_window=False)
print(len(sliding_windows_ds))
for x, y, window_ind in sliding_windows_ds:
print(x.shape, y, window_ind)
break
Out:
/home/circleci/project/braindecode/datautil/windowers.py:245: UserWarning: Meaning of `trial_stop_offset_samples`=0 has changed, use `None` to indicate end of trial/recording. Using `None`.
'Meaning of `trial_stop_offset_samples`=0 has changed, use `None` '
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
Adding metadata with 4 columns
Replacing existing metadata with 4 columns
39 matching events found
No baseline correction applied
0 projection items activated
Loading data for 39 events and 1200 original time points ...
0 bad epochs dropped
468
Loading data for 1 events and 1200 original time points ...
(23, 1200) -1 [0, 0, 1200]
Transforms can also be applied on windows in the same way as shown above on continuous data:
def crop_windows(windows, start_offset_samples, stop_offset_samples):
fs = windows.info["sfreq"]
windows.crop(tmin=start_offset_samples / fs, tmax=stop_offset_samples / fs,
include_tmax=False)
epochs_transform_list = [
MNEPreproc("pick_types", eeg=True, meg=False, stim=False),
MNEPreproc(crop_windows, start_offset_samples=100, stop_offset_samples=900),
]
print(windows_ds.datasets[0].windows.info["ch_names"],
len(windows_ds.datasets[0].windows.times))
preprocess(windows_ds, epochs_transform_list)
print(windows_ds.datasets[0].windows.info["ch_names"],
len(windows_ds.datasets[0].windows.times))
max_i = 2
fig, ax_arr = plt.subplots(1, max_i+1, figsize=((max_i+1)*7, 5),
sharex=True, sharey=True)
for i, (x, y, window_ind) in enumerate(windows_ds):
ax_arr[i].plot(x.T)
ax_arr[i].set_ylim(-0.0002, 0.0002)
ax_arr[i].set_title(f"label={y}")
if i == max_i:
break

Out:
['Fz', 'FC3', 'FC1', 'FCz', 'FC2', 'FC4', 'C5', 'C3', 'C1', 'Cz', 'C2', 'C4', 'C6', 'CP3', 'CP1', 'CPz', 'CP2', 'CP4', 'P1', 'Pz', 'P2', 'POz', 'stim'] 400
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
Loading data for 96 events and 400 original time points ...
/home/circleci/project/examples/plot_dataset_example.py:105: RuntimeWarning: tmax is not in epochs time interval. tmax is set to epochs.tmax
include_tmax=False)
['Fz', 'FC3', 'FC1', 'FCz', 'FC2', 'FC4', 'C5', 'C3', 'C1', 'Cz', 'C2', 'C4', 'C6', 'CP3', 'CP1', 'CPz', 'CP2', 'CP4', 'P1', 'Pz', 'P2', 'POz'] 299
Again, we can easily split windows_ds based on some criteria in the description DataFrame:
subsets = windows_ds.split("session")
print({subset_name: len(subset) for subset_name, subset in subsets.items()})
Out:
{'session_E': 576, 'session_T': 576}
Total running time of the script: ( 0 minutes 8.049 seconds)
Estimated memory usage: 402 MB