braindecode 0.1.4__tar.gz → 0.1.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/PKG-INFO +2 -2
- Braindecode-0.1.4.2/Braindecode.egg-info/SOURCES.txt +53 -0
- Braindecode-0.1.4.2/Braindecode.egg-info/requires.txt +6 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/PKG-INFO +2 -2
- Braindecode-0.1.4.2/braindecode/datasets/bbci.py +337 -0
- Braindecode-0.1.4.2/braindecode/datasets/multiple.py +38 -0
- {Braindecode-0.1.4/braindecode/datautils → Braindecode-0.1.4.2/braindecode/datautil}/iterators.py +14 -11
- Braindecode-0.1.4.2/braindecode/datautil/signalproc.py +145 -0
- Braindecode-0.1.4.2/braindecode/datautil/splitters.py +33 -0
- Braindecode-0.1.4.2/braindecode/datautil/trial_segment.py +220 -0
- Braindecode-0.1.4.2/braindecode/datautil/util.py +2 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/experiments/experiment.py +32 -32
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/experiments/monitors.py +23 -18
- Braindecode-0.1.4.2/braindecode/mne_ext/signalproc.py +58 -0
- Braindecode-0.1.4.2/braindecode/models/__init__.py +0 -0
- Braindecode-0.1.4.2/braindecode/models/deep4.py +159 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/models/shallow_fbcsp.py +16 -6
- Braindecode-0.1.4.2/braindecode/torch_ext/__init__.py +0 -0
- {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/functions.py +4 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/conf.py +1 -1
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/index.rst +7 -4
- Braindecode-0.1.4.2/docs/notebooks/BBCI_Data.ipynb +683 -0
- Braindecode-0.1.4.2/docs/notebooks/BBCI_Data_Start_Stop.ipynb +877 -0
- Braindecode-0.1.4.2/docs/notebooks/Cropped_Decoding.ipynb +500 -0
- Braindecode-0.1.4.2/docs/notebooks/Experiment_Class.ipynb +819 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/notebooks/TrialWise_Decoding.ipynb +7 -9
- Braindecode-0.1.4.2/docs/source/braindecode.datasets.rst +30 -0
- Braindecode-0.1.4.2/docs/source/braindecode.datautil.rst +62 -0
- Braindecode-0.1.4.2/docs/source/braindecode.mne_ext.rst +22 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/braindecode.models.rst +8 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/braindecode.rst +4 -2
- Braindecode-0.1.4.2/docs/source/braindecode.torch_ext.rst +54 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/setup.py +4 -4
- Braindecode-0.1.4/Braindecode.egg-info/SOURCES.txt +0 -38
- Braindecode-0.1.4/Braindecode.egg-info/requires.txt +0 -3
- Braindecode-0.1.4/braindecode/datautils/splitters.py +0 -18
- Braindecode-0.1.4/docs/source/braindecode.datautils.rst +0 -38
- Braindecode-0.1.4/docs/source/braindecode.torchext.rst +0 -54
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/dependency_links.txt +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/not-zip-safe +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/top_level.txt +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/LICENSE.txt +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/MANIFEST.in +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/README.rst +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/__init__.py +0 -0
- {Braindecode-0.1.4/braindecode/datautils → Braindecode-0.1.4.2/braindecode/datasets}/__init__.py +0 -0
- {Braindecode-0.1.4/braindecode/experiments → Braindecode-0.1.4.2/braindecode/datautil}/__init__.py +0 -0
- {Braindecode-0.1.4/braindecode/datautils → Braindecode-0.1.4.2/braindecode/datautil}/signal_target.py +0 -0
- {Braindecode-0.1.4/braindecode/models → Braindecode-0.1.4.2/braindecode/experiments}/__init__.py +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/experiments/stopcriteria.py +0 -0
- {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/mne_ext}/__init__.py +0 -0
- {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/constraints.py +0 -0
- {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/losses.py +0 -0
- {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/modules.py +0 -0
- {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/util.py +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/util.py +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/Makefile +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/braindecode.experiments.rst +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/modules.rst +0 -0
- {Braindecode-0.1.4 → Braindecode-0.1.4.2}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 1.1
|
|
2
2
|
Name: Braindecode
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: A deep learning
|
|
3
|
+
Version: 0.1.4.2
|
|
4
|
+
Summary: A deep learning toolbox to decode raw time-domain EEG.
|
|
5
5
|
Home-page: https://github.com/robintibor/braindecode
|
|
6
6
|
Author: Robin Tibor Schirrmeister
|
|
7
7
|
Author-email: robintibor@gmail.com
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.rst
|
|
4
|
+
setup.py
|
|
5
|
+
Braindecode.egg-info/PKG-INFO
|
|
6
|
+
Braindecode.egg-info/SOURCES.txt
|
|
7
|
+
Braindecode.egg-info/dependency_links.txt
|
|
8
|
+
Braindecode.egg-info/not-zip-safe
|
|
9
|
+
Braindecode.egg-info/requires.txt
|
|
10
|
+
Braindecode.egg-info/top_level.txt
|
|
11
|
+
braindecode/__init__.py
|
|
12
|
+
braindecode/util.py
|
|
13
|
+
braindecode/datasets/__init__.py
|
|
14
|
+
braindecode/datasets/bbci.py
|
|
15
|
+
braindecode/datasets/multiple.py
|
|
16
|
+
braindecode/datautil/__init__.py
|
|
17
|
+
braindecode/datautil/iterators.py
|
|
18
|
+
braindecode/datautil/signal_target.py
|
|
19
|
+
braindecode/datautil/signalproc.py
|
|
20
|
+
braindecode/datautil/splitters.py
|
|
21
|
+
braindecode/datautil/trial_segment.py
|
|
22
|
+
braindecode/datautil/util.py
|
|
23
|
+
braindecode/experiments/__init__.py
|
|
24
|
+
braindecode/experiments/experiment.py
|
|
25
|
+
braindecode/experiments/monitors.py
|
|
26
|
+
braindecode/experiments/stopcriteria.py
|
|
27
|
+
braindecode/mne_ext/__init__.py
|
|
28
|
+
braindecode/mne_ext/signalproc.py
|
|
29
|
+
braindecode/models/__init__.py
|
|
30
|
+
braindecode/models/deep4.py
|
|
31
|
+
braindecode/models/shallow_fbcsp.py
|
|
32
|
+
braindecode/torch_ext/__init__.py
|
|
33
|
+
braindecode/torch_ext/constraints.py
|
|
34
|
+
braindecode/torch_ext/functions.py
|
|
35
|
+
braindecode/torch_ext/losses.py
|
|
36
|
+
braindecode/torch_ext/modules.py
|
|
37
|
+
braindecode/torch_ext/util.py
|
|
38
|
+
docs/Makefile
|
|
39
|
+
docs/conf.py
|
|
40
|
+
docs/index.rst
|
|
41
|
+
docs/notebooks/BBCI_Data.ipynb
|
|
42
|
+
docs/notebooks/BBCI_Data_Start_Stop.ipynb
|
|
43
|
+
docs/notebooks/Cropped_Decoding.ipynb
|
|
44
|
+
docs/notebooks/Experiment_Class.ipynb
|
|
45
|
+
docs/notebooks/TrialWise_Decoding.ipynb
|
|
46
|
+
docs/source/braindecode.datasets.rst
|
|
47
|
+
docs/source/braindecode.datautil.rst
|
|
48
|
+
docs/source/braindecode.experiments.rst
|
|
49
|
+
docs/source/braindecode.mne_ext.rst
|
|
50
|
+
docs/source/braindecode.models.rst
|
|
51
|
+
docs/source/braindecode.rst
|
|
52
|
+
docs/source/braindecode.torch_ext.rst
|
|
53
|
+
docs/source/modules.rst
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 1.1
|
|
2
2
|
Name: Braindecode
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: A deep learning
|
|
3
|
+
Version: 0.1.4.2
|
|
4
|
+
Summary: A deep learning toolbox to decode raw time-domain EEG.
|
|
5
5
|
Home-page: https://github.com/robintibor/braindecode
|
|
6
6
|
Author: Robin Tibor Schirrmeister
|
|
7
7
|
Author-email: robintibor@gmail.com
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from glob import glob
|
|
4
|
+
import os.path
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import h5py
|
|
8
|
+
import mne
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BBCIDataset(object):
|
|
14
|
+
def __init__(self, filename, load_sensor_names=None, check_class_names=False):
|
|
15
|
+
self.__dict__.update(locals())
|
|
16
|
+
del self.self
|
|
17
|
+
|
|
18
|
+
def load(self):
|
|
19
|
+
cnt = self.load_continuous_signal()
|
|
20
|
+
cnt = self.add_markers(cnt)
|
|
21
|
+
return cnt
|
|
22
|
+
|
|
23
|
+
def load_continuous_signal(self):
|
|
24
|
+
wanted_chan_inds, wanted_sensor_names = self.determine_sensors()
|
|
25
|
+
fs = self.determine_samplingrate()
|
|
26
|
+
with h5py.File(self.filename, 'r') as h5file:
|
|
27
|
+
samples = int(h5file['nfo']['T'][0, 0])
|
|
28
|
+
cnt_signal_shape = (samples, len(wanted_chan_inds))
|
|
29
|
+
continuous_signal = np.ones(cnt_signal_shape,
|
|
30
|
+
dtype=np.float32) * np.nan
|
|
31
|
+
for chan_ind_arr, chan_ind_set in enumerate(wanted_chan_inds):
|
|
32
|
+
# + 1 because matlab/this hdf5-naming logic
|
|
33
|
+
# has 1-based indexing
|
|
34
|
+
# i.e ch1,ch2,....
|
|
35
|
+
chan_set_name = 'ch' + str(chan_ind_set + 1)
|
|
36
|
+
# first 0 to unpack into vector, before it is 1xN matrix
|
|
37
|
+
chan_signal = h5file[chan_set_name][
|
|
38
|
+
:].squeeze() # already load into memory
|
|
39
|
+
continuous_signal[:, chan_ind_arr] = chan_signal
|
|
40
|
+
assert not np.any(
|
|
41
|
+
np.isnan(continuous_signal)), "No NaNs expected in signal"
|
|
42
|
+
|
|
43
|
+
# Assume we cant know channel type here automatically
|
|
44
|
+
ch_types = ['misc'] * len(wanted_chan_inds)
|
|
45
|
+
info = mne.create_info(ch_names=wanted_sensor_names, sfreq=fs,
|
|
46
|
+
ch_types=ch_types)
|
|
47
|
+
|
|
48
|
+
cnt = mne.io.RawArray(continuous_signal.T, info)
|
|
49
|
+
return cnt
|
|
50
|
+
|
|
51
|
+
def determine_sensors(self):
|
|
52
|
+
all_sensor_names = self.get_all_sensors(self.filename, pattern=None)
|
|
53
|
+
if self.load_sensor_names is None:
|
|
54
|
+
|
|
55
|
+
# if no sensor names given, take all EEG-chans
|
|
56
|
+
eeg_sensor_names = all_sensor_names
|
|
57
|
+
eeg_sensor_names = filter(lambda s: not s.startswith('BIP'),
|
|
58
|
+
eeg_sensor_names)
|
|
59
|
+
eeg_sensor_names = filter(lambda s: not s.startswith('E'),
|
|
60
|
+
eeg_sensor_names)
|
|
61
|
+
eeg_sensor_names = filter(lambda s: not s.startswith('Microphone'),
|
|
62
|
+
eeg_sensor_names)
|
|
63
|
+
eeg_sensor_names = filter(lambda s: not s.startswith('Breath'),
|
|
64
|
+
eeg_sensor_names)
|
|
65
|
+
eeg_sensor_names = filter(lambda s: not s.startswith('GSR'),
|
|
66
|
+
eeg_sensor_names)
|
|
67
|
+
eeg_sensor_names = list(eeg_sensor_names)
|
|
68
|
+
assert (len(eeg_sensor_names) == 128 or
|
|
69
|
+
len(eeg_sensor_names) == 64 or
|
|
70
|
+
len(eeg_sensor_names) == 32 or
|
|
71
|
+
len(eeg_sensor_names) == 16), (
|
|
72
|
+
"Recheck this code if you have different sensors...")
|
|
73
|
+
self.load_sensor_names = eeg_sensor_names
|
|
74
|
+
chan_inds = self.determine_chan_inds(all_sensor_names,
|
|
75
|
+
self.load_sensor_names)
|
|
76
|
+
return chan_inds, self.load_sensor_names
|
|
77
|
+
|
|
78
|
+
def determine_samplingrate(self):
|
|
79
|
+
with h5py.File(self.filename, 'r') as h5file:
|
|
80
|
+
fs = h5file['dat']['fs'][0, 0]
|
|
81
|
+
assert isinstance(fs, int) or fs.is_integer()
|
|
82
|
+
fs = int(fs)
|
|
83
|
+
return fs
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def determine_chan_inds(all_sensor_names, sensor_names):
|
|
87
|
+
assert sensor_names is not None
|
|
88
|
+
chan_inds = [all_sensor_names.index(s) for s in sensor_names]
|
|
89
|
+
assert len(chan_inds) == len(sensor_names), ("All"
|
|
90
|
+
"sensors should be there.")
|
|
91
|
+
assert len(set(chan_inds)) == len(chan_inds), ("No"
|
|
92
|
+
"duplicated sensors wanted.")
|
|
93
|
+
return chan_inds
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def get_all_sensors(filename, pattern=None):
|
|
97
|
+
with h5py.File(filename, 'r') as h5file:
|
|
98
|
+
clab_set = h5file['dat']['clab'][:].squeeze()
|
|
99
|
+
all_sensor_names = [''.join(chr(c) for c in h5file[obj_ref]) for
|
|
100
|
+
obj_ref in clab_set]
|
|
101
|
+
if pattern is not None:
|
|
102
|
+
all_sensor_names = filter(
|
|
103
|
+
lambda sname: re.search(pattern, sname),
|
|
104
|
+
all_sensor_names)
|
|
105
|
+
return all_sensor_names
|
|
106
|
+
|
|
107
|
+
def add_markers(self, cnt):
|
|
108
|
+
with h5py.File(self.filename, 'r') as h5file:
|
|
109
|
+
event_times_in_ms = h5file['mrk']['time'][:].squeeze()
|
|
110
|
+
event_classes = h5file['mrk']['event']['desc'][:].squeeze()
|
|
111
|
+
|
|
112
|
+
# Check whether class names known and correct order
|
|
113
|
+
class_name_set = h5file['nfo']['className'][:].squeeze()
|
|
114
|
+
all_class_names = [''.join(chr(c) for c in h5file[obj_ref])
|
|
115
|
+
for obj_ref in class_name_set]
|
|
116
|
+
|
|
117
|
+
if self.check_class_names:
|
|
118
|
+
check_class_names(
|
|
119
|
+
all_class_names, event_times_in_ms, event_classes,)
|
|
120
|
+
|
|
121
|
+
event_times_in_samples = event_times_in_ms * cnt.info['sfreq'] / 1000.0
|
|
122
|
+
event_times_in_samples = np.uint32(np.round(event_times_in_samples))
|
|
123
|
+
|
|
124
|
+
# Check if there are markers at the same time
|
|
125
|
+
previous_i_sample = -1
|
|
126
|
+
for i_event, (i_sample, id_class) in enumerate(
|
|
127
|
+
zip(event_times_in_samples, event_classes)):
|
|
128
|
+
if i_sample == previous_i_sample:
|
|
129
|
+
log.warning("Same sample has at least two markers.\n"
|
|
130
|
+
"{:d}: ({:d} and {:d}).\n".format(i_sample,
|
|
131
|
+
event_classes[
|
|
132
|
+
i_event - 1],
|
|
133
|
+
event_classes[
|
|
134
|
+
i_event]) +
|
|
135
|
+
"Marker codes will be summed.")
|
|
136
|
+
previous_i_sample = i_sample
|
|
137
|
+
|
|
138
|
+
# Now create stim chan
|
|
139
|
+
stim_chan = np.zeros_like(cnt.get_data()[0])
|
|
140
|
+
for i_sample, id_class in zip(event_times_in_samples, event_classes):
|
|
141
|
+
stim_chan[i_sample] += id_class
|
|
142
|
+
info = mne.create_info(ch_names=['STI 014'],
|
|
143
|
+
sfreq=cnt.info['sfreq'],
|
|
144
|
+
ch_types=['stim'])
|
|
145
|
+
stim_cnt = mne.io.RawArray(stim_chan[None], info, verbose='WARNING')
|
|
146
|
+
cnt = cnt.add_channels([stim_cnt])
|
|
147
|
+
event_arr = [event_times_in_samples,
|
|
148
|
+
[0] * len(event_times_in_samples),
|
|
149
|
+
event_classes]
|
|
150
|
+
cnt.info['events'] = np.array(event_arr).T
|
|
151
|
+
return cnt
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def check_class_names(all_class_names, event_times_in_ms, event_classes,):
|
|
155
|
+
if all_class_names == ['Right Hand', 'Left Hand', 'Rest', 'Feet']:
|
|
156
|
+
pass
|
|
157
|
+
elif ((all_class_names == ['1', '10', '11', '111', '12', '13',
|
|
158
|
+
'150',
|
|
159
|
+
'2', '20', '22', '3', '30', '33', '4',
|
|
160
|
+
'40', '44', '99']) or
|
|
161
|
+
(all_class_names == ['1', '10', '11', '12', '13', '150',
|
|
162
|
+
'2', '20', '22', '3', '30', '33',
|
|
163
|
+
'4', '40', '44', '99']) or
|
|
164
|
+
(all_class_names == ['1', '2', '3', '4'])):
|
|
165
|
+
pass # Semantic classes
|
|
166
|
+
elif all_class_names == ['Rest', 'Feet', 'Left Hand', 'Right Hand']:
|
|
167
|
+
# Have to swap from
|
|
168
|
+
# ['Rest', 'Feet', 'Left Hand', 'Right Hand']
|
|
169
|
+
# to
|
|
170
|
+
# ['Right Hand', 'Left Hand', 'Rest', 'Feet']
|
|
171
|
+
right_mask = event_classes == 4
|
|
172
|
+
left_mask = event_classes == 3
|
|
173
|
+
rest_mask = event_classes == 1
|
|
174
|
+
feet_mask = event_classes == 2
|
|
175
|
+
event_classes[right_mask] = 1
|
|
176
|
+
event_classes[left_mask] = 2
|
|
177
|
+
event_classes[rest_mask] = 3
|
|
178
|
+
event_classes[feet_mask] = 4
|
|
179
|
+
elif all_class_names == ['Right Hand Start', 'Left Hand Start',
|
|
180
|
+
'Rest Start', 'Feet Start',
|
|
181
|
+
'Right Hand End',
|
|
182
|
+
'Left Hand End', 'Rest End', 'Feet End']:
|
|
183
|
+
pass
|
|
184
|
+
elif all_class_names == ['Right Hand', 'Left Hand', 'Rest',
|
|
185
|
+
'Feet', 'Face', 'Navigation', 'Music',
|
|
186
|
+
'Rotation',
|
|
187
|
+
'Subtraction', 'Words']:
|
|
188
|
+
pass # robot hall 10 class decoding
|
|
189
|
+
elif (all_class_names == ['RightHand', 'Feet', 'Rotation', 'Words',
|
|
190
|
+
'\x00\x00', '\x00\x00', '\x00\x00',
|
|
191
|
+
'\x00\x00', '\x00\x00',
|
|
192
|
+
'RightHand_End', '\x00\x00', '\x00\x00',
|
|
193
|
+
'\x00\x00', '\x00\x00',
|
|
194
|
+
'\x00\x00', '\x00\x00', '\x00\x00',
|
|
195
|
+
'\x00\x00', '\x00\x00',
|
|
196
|
+
'Feet_End', '\x00\x00', '\x00\x00',
|
|
197
|
+
'\x00\x00', '\x00\x00',
|
|
198
|
+
'\x00\x00', '\x00\x00', '\x00\x00',
|
|
199
|
+
'\x00\x00', '\x00\x00',
|
|
200
|
+
'Rotation_End', '\x00\x00', '\x00\x00',
|
|
201
|
+
'\x00\x00', '\x00\x00',
|
|
202
|
+
'\x00\x00', '\x00\x00', '\x00\x00',
|
|
203
|
+
'\x00\x00', '\x00\x00',
|
|
204
|
+
'Words_End'] or
|
|
205
|
+
all_class_names == ['RightHand', 'Feet', 'Rotation',
|
|
206
|
+
'Words',
|
|
207
|
+
'Rest', '\x00\x00', '\x00\x00',
|
|
208
|
+
'\x00\x00', '\x00\x00',
|
|
209
|
+
'RightHand_End', '\x00\x00',
|
|
210
|
+
'\x00\x00', '\x00\x00',
|
|
211
|
+
'\x00\x00', '\x00\x00',
|
|
212
|
+
'\x00\x00', '\x00\x00',
|
|
213
|
+
'\x00\x00',
|
|
214
|
+
'\x00\x00', 'Feet_End',
|
|
215
|
+
'\x00\x00', '\x00\x00',
|
|
216
|
+
'\x00\x00',
|
|
217
|
+
'\x00\x00', '\x00\x00',
|
|
218
|
+
'\x00\x00', '\x00\x00',
|
|
219
|
+
'\x00\x00',
|
|
220
|
+
'\x00\x00', 'Rotation_End',
|
|
221
|
+
'\x00\x00', '\x00\x00',
|
|
222
|
+
'\x00\x00', '\x00\x00',
|
|
223
|
+
'\x00\x00', '\x00\x00',
|
|
224
|
+
'\x00\x00',
|
|
225
|
+
'\x00\x00', '\x00\x00',
|
|
226
|
+
'Words_End', '\x00\x00',
|
|
227
|
+
'\x00\x00',
|
|
228
|
+
'\x00\x00', '\x00\x00',
|
|
229
|
+
'\x00\x00', '\x00\x00',
|
|
230
|
+
'\x00\x00',
|
|
231
|
+
'\x00\x00', '\x00\x00',
|
|
232
|
+
'Rest_End']):
|
|
233
|
+
pass # weird stuff when we recorded cursor in robot hall
|
|
234
|
+
# on 2016-09-14 and 2016-09-16 :D
|
|
235
|
+
|
|
236
|
+
elif (all_class_names == ['0004', '0016', '0032', '0056', '0064',
|
|
237
|
+
'0088', '0095', '0120']):
|
|
238
|
+
pass
|
|
239
|
+
elif (all_class_names == ['0004', '0056', '0088', '0120']):
|
|
240
|
+
pass
|
|
241
|
+
elif (all_class_names == ['0004', '0016', '0032', '0048', '0056',
|
|
242
|
+
'0064', '0080', '0088', '0095', '0120']):
|
|
243
|
+
pass
|
|
244
|
+
elif (all_class_names == ['0004', '0016', '0056', '0088', '0120',
|
|
245
|
+
'__']):
|
|
246
|
+
pass
|
|
247
|
+
elif (all_class_names == ['0004', '0056', '0088', '0120', '__']):
|
|
248
|
+
pass
|
|
249
|
+
elif (all_class_names == ['0004', '0032', '0048', '0056', '0064',
|
|
250
|
+
'0080', '0088', '0095', '0120', '__']):
|
|
251
|
+
pass
|
|
252
|
+
elif (all_class_names == ['0004', '0056', '0080', '0088', '0096',
|
|
253
|
+
'0120', '__']):
|
|
254
|
+
pass
|
|
255
|
+
elif (all_class_names == ['0004', '0032', '0056', '0064', '0080',
|
|
256
|
+
'0088', '0095', '0120']):
|
|
257
|
+
pass
|
|
258
|
+
elif (all_class_names == ['0004', '0032', '0048', '0056', '0064',
|
|
259
|
+
'0080', '0088', '0095', '0120']):
|
|
260
|
+
pass
|
|
261
|
+
elif (all_class_names == ['0004', '0016', '0032', '0048', '0056',
|
|
262
|
+
'0064', '0080', '0088', '0095', '0096',
|
|
263
|
+
'0120']):
|
|
264
|
+
pass
|
|
265
|
+
elif (all_class_names == ['4', '16', '32', '56', '64', '88', '95',
|
|
266
|
+
'120']):
|
|
267
|
+
pass
|
|
268
|
+
elif (all_class_names == ['4', '56', '88', '120']):
|
|
269
|
+
pass
|
|
270
|
+
elif (all_class_names == ['4', '16', '32', '48', '56',
|
|
271
|
+
'64', '80', '88', '95', '120']):
|
|
272
|
+
pass
|
|
273
|
+
elif (all_class_names == ['0', '4', '56', '88', '120']):
|
|
274
|
+
pass
|
|
275
|
+
elif (all_class_names == ['0', '4', '16', '56', '88', '120']):
|
|
276
|
+
pass
|
|
277
|
+
elif (all_class_names == ['0', '4', '32', '48', '56', '64', '80',
|
|
278
|
+
'88', '95', '120']):
|
|
279
|
+
pass
|
|
280
|
+
elif (all_class_names == ['0', '4', '56', '80', '88', '96', '120']):
|
|
281
|
+
pass
|
|
282
|
+
elif (all_class_names == ['4', '32', '56', '64', '80', '88', '95',
|
|
283
|
+
'120']):
|
|
284
|
+
pass
|
|
285
|
+
elif (all_class_names == ['One', 'Two', 'Three', 'Four']):
|
|
286
|
+
pass
|
|
287
|
+
elif (
|
|
288
|
+
all_class_names == ['1', '10', '11', '12', '2', '20', '3', '30',
|
|
289
|
+
'4', '40']):
|
|
290
|
+
pass
|
|
291
|
+
elif (
|
|
292
|
+
all_class_names == ['1', '10', '12', '13', '2', '20', '3', '30',
|
|
293
|
+
'4', '40']):
|
|
294
|
+
pass
|
|
295
|
+
elif (
|
|
296
|
+
all_class_names == ['1', '10', '13', '2', '20', '3', '30', '4',
|
|
297
|
+
'40', '99']):
|
|
298
|
+
pass
|
|
299
|
+
elif (all_class_names == ['1', '10', '11', '14', '18', '20', '21',
|
|
300
|
+
'24', '251', '252', '28', '30', '4',
|
|
301
|
+
'8']):
|
|
302
|
+
pass
|
|
303
|
+
elif (all_class_names == ['1', '10', '11', '14', '18', '20', '21',
|
|
304
|
+
'24', '252', '253', '28', '30', '4',
|
|
305
|
+
'8']):
|
|
306
|
+
pass
|
|
307
|
+
elif len(event_times_in_ms) == len(all_class_names):
|
|
308
|
+
pass # weird neuroone(?) logic where class names have event classes
|
|
309
|
+
elif (all_class_names == ['Right_hand_stimulus_onset',
|
|
310
|
+
'Feet_stimulus_onset',
|
|
311
|
+
'Rotation_stimulus_onset',
|
|
312
|
+
'Words_stimulus_onset',
|
|
313
|
+
'Right_hand_stimulus_offset',
|
|
314
|
+
'Feet_stimulus_offset',
|
|
315
|
+
'Rotation_stimulus_offset',
|
|
316
|
+
'Words_stimulus_offset']):
|
|
317
|
+
pass
|
|
318
|
+
else:
|
|
319
|
+
# remove this whole if else stuffs?
|
|
320
|
+
log.warn("Unknown class names {:s}".format(
|
|
321
|
+
all_class_names))
|
|
322
|
+
|
|
323
|
+
def load_bbci_sets_from_folder(folder, runs='all'):
|
|
324
|
+
bbci_mat_files = sorted(glob(os.path.join(folder, '*.BBCI.mat')))
|
|
325
|
+
if runs != 'all':
|
|
326
|
+
file_run_numbers = [int(re.search('S[0-9]{3,3}R[0-9]{2,2}_', f).group()[5:7])
|
|
327
|
+
for f in bbci_mat_files]
|
|
328
|
+
indices = [file_run_numbers.index(num) for num in runs]
|
|
329
|
+
|
|
330
|
+
wanted_files = np.array(bbci_mat_files)[indices]
|
|
331
|
+
else:
|
|
332
|
+
wanted_files = bbci_mat_files
|
|
333
|
+
cnts = []
|
|
334
|
+
for f in wanted_files:
|
|
335
|
+
log.info("Loading {:s}".format(f))
|
|
336
|
+
cnts.append(BBCIDataset(f).load())
|
|
337
|
+
return cnts
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from braindecode.mne_ext.signalproc import resample_cnt, \
|
|
4
|
+
concatenate_raws_with_events
|
|
5
|
+
|
|
6
|
+
log = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MultipleSetLoader(object):
|
|
10
|
+
"""
|
|
11
|
+
Class to load multiple sets.
|
|
12
|
+
Resamples sets down as necessary.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
set_loaders: object with load method: () -> MNE Raw object
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, set_loaders):
|
|
20
|
+
self.set_loaders = set_loaders
|
|
21
|
+
|
|
22
|
+
def load(self):
|
|
23
|
+
cnt = self.set_loaders[0].load()
|
|
24
|
+
for loader in self.set_loaders[1:]:
|
|
25
|
+
next_cnt = loader.load()
|
|
26
|
+
# always sample down to lowest common denominator
|
|
27
|
+
if next_cnt.fs > cnt.fs:
|
|
28
|
+
log.warning("Next set has larger sampling rate ({:d}) "
|
|
29
|
+
"than before ({:d}), resampling next set".format(
|
|
30
|
+
next_cnt.fs, cnt.fs))
|
|
31
|
+
next_cnt = resample_cnt(next_cnt, cnt.fs)
|
|
32
|
+
if next_cnt.fs < cnt.fs:
|
|
33
|
+
log.warning("Next set has smaller sampling rate ({:d}) "
|
|
34
|
+
"than before ({:d}), resampling set so far".format(
|
|
35
|
+
next_cnt.fs, cnt.fs))
|
|
36
|
+
cnt = resample_cnt(cnt, next_cnt.fs)
|
|
37
|
+
cnt = concatenate_raws_with_events(cnt, next_cnt)
|
|
38
|
+
return cnt
|
{Braindecode-0.1.4/braindecode/datautils → Braindecode-0.1.4.2/braindecode/datautil}/iterators.py
RENAMED
|
@@ -137,13 +137,18 @@ class CropsFromTrialsIterator(object):
|
|
|
137
137
|
for i_trial, block in
|
|
138
138
|
enumerate(start_stop_blocks_per_trial)
|
|
139
139
|
for (start, stop) in block]
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
i_trial_start_stop_block = np.array(i_trial_start_stop_block)
|
|
141
|
+
if i_trial_start_stop_block.ndim == 1:
|
|
142
|
+
i_trial_start_stop_block = i_trial_start_stop_block[None,:]
|
|
143
|
+
#if shuffle:
|
|
144
|
+
# self.rng.shuffle(i_trial_start_stop_block)
|
|
142
145
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
blocks_per_batch = get_balanced_batches(len(i_trial_start_stop_block),
|
|
147
|
+
batch_size=self.batch_size,
|
|
148
|
+
rng=self.rng,
|
|
149
|
+
shuffle=shuffle)
|
|
150
|
+
for i_blocks in blocks_per_batch:
|
|
151
|
+
start_stop_blocks = i_trial_start_stop_block[i_blocks]
|
|
147
152
|
batch = create_batch_from_i_trial_start_stop_blocks(
|
|
148
153
|
X, y, start_stop_blocks, self.n_preds_per_input)
|
|
149
154
|
yield batch
|
|
@@ -205,12 +210,10 @@ def create_batch_from_i_trial_start_stop_blocks(X, y, i_trial_start_stop_block,
|
|
|
205
210
|
ys = []
|
|
206
211
|
for i_trial, start, stop in i_trial_start_stop_block:
|
|
207
212
|
Xs.append(X[i_trial][:,start:stop])
|
|
208
|
-
# one-hot-encode targets
|
|
209
|
-
#block_y = np.zeros((n_classes, n_preds_per_input), dtype=np.float32)
|
|
210
|
-
#block_y[y[i_trial]] = 1
|
|
211
|
-
#block_y = np.ones((n_preds_per_input), dtype=np.int64) * y[i_trial]
|
|
212
|
-
#ys.append(block_y)
|
|
213
213
|
ys.append(y[i_trial])
|
|
214
214
|
batch_X = np.array(Xs)
|
|
215
215
|
batch_y = np.array(ys)
|
|
216
|
+
# add empty fourth dimension if necessary
|
|
217
|
+
if batch_X.ndim == 3:
|
|
218
|
+
batch_X = batch_X[:,:,:, None]
|
|
216
219
|
return batch_X, batch_y
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import numpy as np
|
|
5
|
+
import scipy
|
|
6
|
+
|
|
7
|
+
log = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def exponential_running_standardize(data, factor_new=0.001,
|
|
11
|
+
init_block_size=None, eps=1e-4):
|
|
12
|
+
df = pd.DataFrame(data)
|
|
13
|
+
meaned = df.ewm(alpha=factor_new).mean()
|
|
14
|
+
demeaned = df - meaned
|
|
15
|
+
squared = demeaned * demeaned
|
|
16
|
+
square_ewmed = squared.ewm(alpha=factor_new).mean()
|
|
17
|
+
standardized = demeaned / np.maximum(eps, np.sqrt(np.array(square_ewmed)))
|
|
18
|
+
standardized = np.array(standardized)
|
|
19
|
+
if init_block_size is not None:
|
|
20
|
+
other_axis = tuple(range(1, len(data.shape)))
|
|
21
|
+
init_mean = np.mean(data[0:init_block_size], axis=other_axis,
|
|
22
|
+
keepdims=True)
|
|
23
|
+
init_std = np.std(data[0:init_block_size], axis=other_axis,
|
|
24
|
+
keepdims=True)
|
|
25
|
+
init_block_standardized = (data[0:init_block_size] - init_mean) / \
|
|
26
|
+
np.maximum(eps, init_std)
|
|
27
|
+
standardized[0:init_block_size] = init_block_standardized
|
|
28
|
+
return standardized
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def exponential_running_demean(data, factor_new=0.001, init_block_size=None):
|
|
32
|
+
df = pd.DataFrame(data)
|
|
33
|
+
meaned = df.ewm(alpha=factor_new).mean()
|
|
34
|
+
demeaned = df - meaned
|
|
35
|
+
demeaned = np.array(demeaned)
|
|
36
|
+
if init_block_size is not None:
|
|
37
|
+
other_axis = tuple(range(1, len(data.shape)))
|
|
38
|
+
init_mean = np.mean(data[0:init_block_size], axis=other_axis,
|
|
39
|
+
keepdims=True)
|
|
40
|
+
demeaned[0:init_block_size] = (data[0:init_block_size] - init_mean)
|
|
41
|
+
return demeaned
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def highpass_cnt(data, low_cut_hz, fs, filt_order=3, axis=0):
|
|
45
|
+
"""
|
|
46
|
+
Highpass signal using butterworth filter of given order.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
data: 2d-array
|
|
51
|
+
Time x channels
|
|
52
|
+
low_cut_hz: float
|
|
53
|
+
fs: float
|
|
54
|
+
filt_order: int
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
highpassed_data: 2d-array
|
|
59
|
+
Data after applying highpass filter.
|
|
60
|
+
"""
|
|
61
|
+
if (low_cut_hz is None) or (low_cut_hz == 0):
|
|
62
|
+
log.info("Not doing any highpass, since low 0 or None")
|
|
63
|
+
return data.copy()
|
|
64
|
+
b, a = scipy.signal.butter(filt_order, low_cut_hz / (fs / 2.0),
|
|
65
|
+
btype='highpass')
|
|
66
|
+
assert filter_is_stable(a)
|
|
67
|
+
data_highpassed = scipy.signal.lfilter(b, a, data, axis=axis)
|
|
68
|
+
return data_highpassed
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def lowpass_cnt(data, high_cut_hz, fs, filt_order=3, axis=0):
|
|
72
|
+
"""
|
|
73
|
+
Lowpass signal using butterworth filter of given order.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
data: 2d-array
|
|
78
|
+
Time x channels
|
|
79
|
+
high_cut_hz: float
|
|
80
|
+
fs: float
|
|
81
|
+
filt_order: int
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
lowpassed_data: 2d-array
|
|
86
|
+
Data after applying lowpass filter.
|
|
87
|
+
"""
|
|
88
|
+
if (high_cut_hz is None) or (high_cut_hz == fs / 2.0):
|
|
89
|
+
log.info(
|
|
90
|
+
"Not doing any lowpass, since high cut hz is None or nyquist freq.")
|
|
91
|
+
return data.copy()
|
|
92
|
+
b, a = scipy.signal.butter(filt_order, high_cut_hz / (fs / 2.0),
|
|
93
|
+
btype='lowpass')
|
|
94
|
+
assert filter_is_stable(a)
|
|
95
|
+
data_lowpassed = scipy.signal.lfilter(b, a, data, axis=axis)
|
|
96
|
+
return data_lowpassed
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def bandpass_cnt(data, low_cut_hz, high_cut_hz, fs, filt_order=3, axis=0):
|
|
100
|
+
"""
|
|
101
|
+
Bandpass signal using butterworth filter of given order.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
data: 2d-array
|
|
106
|
+
Time x channels
|
|
107
|
+
low_cut_hz: float
|
|
108
|
+
high_cut_hz: float
|
|
109
|
+
fs: float
|
|
110
|
+
filt_order: int
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
bandpassed_data: 2d-array
|
|
115
|
+
Data after applying bandpass filter.
|
|
116
|
+
"""
|
|
117
|
+
if (low_cut_hz == 0 or low_cut_hz is None) and (
|
|
118
|
+
high_cut_hz == None or high_cut_hz == fs / 2.0):
|
|
119
|
+
log.info("Not doing any bandpass, since low 0 or None and "
|
|
120
|
+
"high None or nyquist frequency")
|
|
121
|
+
return data.copy()
|
|
122
|
+
if low_cut_hz == 0 or low_cut_hz == None:
|
|
123
|
+
log.info("Using lowpass filter since low cut hz is 0 or None")
|
|
124
|
+
return lowpass_cnt(data, high_cut_hz, fs, filt_order=filt_order, axis=axis)
|
|
125
|
+
if high_cut_hz == None or high_cut_hz == (fs / 2.0):
|
|
126
|
+
log.info(
|
|
127
|
+
"Using highpass filter since high cut hz is None or nyquist freq")
|
|
128
|
+
return highpass_cnt(data, low_cut_hz, fs, filt_order=filt_order, axis=axis)
|
|
129
|
+
|
|
130
|
+
nyq_freq = 0.5 * fs
|
|
131
|
+
low = low_cut_hz / nyq_freq
|
|
132
|
+
high = high_cut_hz / nyq_freq
|
|
133
|
+
b, a = scipy.signal.butter(filt_order, [low, high], btype='bandpass', axis=axis)
|
|
134
|
+
assert filter_is_stable(a), "Filter should be stable..."
|
|
135
|
+
data_bandpassed = scipy.signal.lfilter(b, a, data, axis=0)
|
|
136
|
+
return data_bandpassed
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def filter_is_stable(a):
|
|
140
|
+
assert a[0] == 1.0, (
|
|
141
|
+
"a[0] should normally be zero, did you accidentally supply b?\n"
|
|
142
|
+
"a: {:s}".format(str(a)))
|
|
143
|
+
# from http://stackoverflow.com/a/8812737/1469195
|
|
144
|
+
return np.all(np.abs(np.roots(a))<1)
|
|
145
|
+
|