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.
Files changed (60) hide show
  1. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/PKG-INFO +2 -2
  2. Braindecode-0.1.4.2/Braindecode.egg-info/SOURCES.txt +53 -0
  3. Braindecode-0.1.4.2/Braindecode.egg-info/requires.txt +6 -0
  4. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/PKG-INFO +2 -2
  5. Braindecode-0.1.4.2/braindecode/datasets/bbci.py +337 -0
  6. Braindecode-0.1.4.2/braindecode/datasets/multiple.py +38 -0
  7. {Braindecode-0.1.4/braindecode/datautils → Braindecode-0.1.4.2/braindecode/datautil}/iterators.py +14 -11
  8. Braindecode-0.1.4.2/braindecode/datautil/signalproc.py +145 -0
  9. Braindecode-0.1.4.2/braindecode/datautil/splitters.py +33 -0
  10. Braindecode-0.1.4.2/braindecode/datautil/trial_segment.py +220 -0
  11. Braindecode-0.1.4.2/braindecode/datautil/util.py +2 -0
  12. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/experiments/experiment.py +32 -32
  13. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/experiments/monitors.py +23 -18
  14. Braindecode-0.1.4.2/braindecode/mne_ext/signalproc.py +58 -0
  15. Braindecode-0.1.4.2/braindecode/models/__init__.py +0 -0
  16. Braindecode-0.1.4.2/braindecode/models/deep4.py +159 -0
  17. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/models/shallow_fbcsp.py +16 -6
  18. Braindecode-0.1.4.2/braindecode/torch_ext/__init__.py +0 -0
  19. {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/functions.py +4 -0
  20. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/conf.py +1 -1
  21. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/index.rst +7 -4
  22. Braindecode-0.1.4.2/docs/notebooks/BBCI_Data.ipynb +683 -0
  23. Braindecode-0.1.4.2/docs/notebooks/BBCI_Data_Start_Stop.ipynb +877 -0
  24. Braindecode-0.1.4.2/docs/notebooks/Cropped_Decoding.ipynb +500 -0
  25. Braindecode-0.1.4.2/docs/notebooks/Experiment_Class.ipynb +819 -0
  26. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/notebooks/TrialWise_Decoding.ipynb +7 -9
  27. Braindecode-0.1.4.2/docs/source/braindecode.datasets.rst +30 -0
  28. Braindecode-0.1.4.2/docs/source/braindecode.datautil.rst +62 -0
  29. Braindecode-0.1.4.2/docs/source/braindecode.mne_ext.rst +22 -0
  30. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/braindecode.models.rst +8 -0
  31. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/braindecode.rst +4 -2
  32. Braindecode-0.1.4.2/docs/source/braindecode.torch_ext.rst +54 -0
  33. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/setup.py +4 -4
  34. Braindecode-0.1.4/Braindecode.egg-info/SOURCES.txt +0 -38
  35. Braindecode-0.1.4/Braindecode.egg-info/requires.txt +0 -3
  36. Braindecode-0.1.4/braindecode/datautils/splitters.py +0 -18
  37. Braindecode-0.1.4/docs/source/braindecode.datautils.rst +0 -38
  38. Braindecode-0.1.4/docs/source/braindecode.torchext.rst +0 -54
  39. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/dependency_links.txt +0 -0
  40. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/not-zip-safe +0 -0
  41. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/Braindecode.egg-info/top_level.txt +0 -0
  42. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/LICENSE.txt +0 -0
  43. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/MANIFEST.in +0 -0
  44. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/README.rst +0 -0
  45. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/__init__.py +0 -0
  46. {Braindecode-0.1.4/braindecode/datautils → Braindecode-0.1.4.2/braindecode/datasets}/__init__.py +0 -0
  47. {Braindecode-0.1.4/braindecode/experiments → Braindecode-0.1.4.2/braindecode/datautil}/__init__.py +0 -0
  48. {Braindecode-0.1.4/braindecode/datautils → Braindecode-0.1.4.2/braindecode/datautil}/signal_target.py +0 -0
  49. {Braindecode-0.1.4/braindecode/models → Braindecode-0.1.4.2/braindecode/experiments}/__init__.py +0 -0
  50. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/experiments/stopcriteria.py +0 -0
  51. {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/mne_ext}/__init__.py +0 -0
  52. {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/constraints.py +0 -0
  53. {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/losses.py +0 -0
  54. {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/modules.py +0 -0
  55. {Braindecode-0.1.4/braindecode/torchext → Braindecode-0.1.4.2/braindecode/torch_ext}/util.py +0 -0
  56. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/braindecode/util.py +0 -0
  57. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/Makefile +0 -0
  58. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/braindecode.experiments.rst +0 -0
  59. {Braindecode-0.1.4 → Braindecode-0.1.4.2}/docs/source/modules.rst +0 -0
  60. {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 for raw time-domain EEG decoding toolbox.
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
@@ -0,0 +1,6 @@
1
+ numpy
2
+ mne
3
+ torch
4
+ h5py
5
+ pandas
6
+ resampy
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: Braindecode
3
- Version: 0.1.4
4
- Summary: A deep learning for raw time-domain EEG decoding toolbox.
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
@@ -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
- if shuffle:
141
- self.rng.shuffle(i_trial_start_stop_block)
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
- for i_block in range(0, len(i_trial_start_stop_block), self.batch_size):
144
- i_block_stop = min(i_block + self.batch_size,
145
- len(i_trial_start_stop_block))
146
- start_stop_blocks = i_trial_start_stop_block[i_block:i_block_stop]
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
+