braindecode 0.5.1__py3-none-any.whl

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 (69) hide show
  1. Braindecode-0.5.1.dist-info/LICENSE.txt +31 -0
  2. Braindecode-0.5.1.dist-info/METADATA +133 -0
  3. Braindecode-0.5.1.dist-info/RECORD +69 -0
  4. Braindecode-0.5.1.dist-info/WHEEL +5 -0
  5. Braindecode-0.5.1.dist-info/top_level.txt +2 -0
  6. braindecode/__init__.py +4 -0
  7. braindecode/classifier.py +258 -0
  8. braindecode/datasets/__init__.py +7 -0
  9. braindecode/datasets/base.py +293 -0
  10. braindecode/datasets/bbci.py +679 -0
  11. braindecode/datasets/moabb.py +110 -0
  12. braindecode/datasets/sleep_physionet.py +103 -0
  13. braindecode/datasets/tuh.py +230 -0
  14. braindecode/datautil/__init__.py +11 -0
  15. braindecode/datautil/mne.py +136 -0
  16. braindecode/datautil/preprocess.py +364 -0
  17. braindecode/datautil/serialization.py +111 -0
  18. braindecode/datautil/util.py +36 -0
  19. braindecode/datautil/windowers.py +435 -0
  20. braindecode/datautil/xy.py +77 -0
  21. braindecode/models/__init__.py +12 -0
  22. braindecode/models/deep4.py +252 -0
  23. braindecode/models/eegnet.py +339 -0
  24. braindecode/models/eegresnet.py +232 -0
  25. braindecode/models/functions.py +43 -0
  26. braindecode/models/hybrid.py +103 -0
  27. braindecode/models/modules.py +150 -0
  28. braindecode/models/shallow_fbcsp.py +158 -0
  29. braindecode/models/sleep_stager_chambon_2018.py +113 -0
  30. braindecode/models/tcn.py +143 -0
  31. braindecode/models/util.py +66 -0
  32. braindecode/regressor.py +240 -0
  33. braindecode/samplers/__init__.py +5 -0
  34. braindecode/samplers/base.py +78 -0
  35. braindecode/samplers/ssl.py +118 -0
  36. braindecode/training/__init__.py +8 -0
  37. braindecode/training/callbacks.py +21 -0
  38. braindecode/training/losses.py +17 -0
  39. braindecode/training/scoring.py +296 -0
  40. braindecode/util.py +362 -0
  41. braindecode/version.py +1 -0
  42. braindecode/visualization/__init__.py +5 -0
  43. braindecode/visualization/gradients.py +53 -0
  44. test/__init__.py +0 -0
  45. test/acceptance_tests/__init__.py +0 -0
  46. test/acceptance_tests/test_cropped_decoding.py +171 -0
  47. test/acceptance_tests/test_eeg_classifier.py +255 -0
  48. test/acceptance_tests/test_model_architectures.py +54 -0
  49. test/acceptance_tests/test_trialwise_decoding.py +168 -0
  50. test/unit_tests/__init__.py +0 -0
  51. test/unit_tests/datasets/__init__.py +0 -0
  52. test/unit_tests/datasets/test_dataset.py +227 -0
  53. test/unit_tests/datasets/test_mne.py +145 -0
  54. test/unit_tests/datasets/test_sleep_physionet.py +23 -0
  55. test/unit_tests/datasets/test_tuh.py +97 -0
  56. test/unit_tests/datasets/test_xy.py +31 -0
  57. test/unit_tests/datautil/__init__.py +0 -0
  58. test/unit_tests/datautil/test_preprocess.py +270 -0
  59. test/unit_tests/datautil/test_serialization.py +127 -0
  60. test/unit_tests/datautil/test_windowers.py +519 -0
  61. test/unit_tests/models/__init__.py +0 -0
  62. test/unit_tests/models/test_models.py +136 -0
  63. test/unit_tests/models/test_util.py +22 -0
  64. test/unit_tests/test_eeg_classifier.py +73 -0
  65. test/unit_tests/test_eeg_regressor.py +72 -0
  66. test/unit_tests/test_scoring.py +282 -0
  67. test/unit_tests/test_util.py +30 -0
  68. test/unit_tests/visualization/__init__.py +0 -0
  69. test/unit_tests/visualization/test_gradients.py +38 -0
@@ -0,0 +1,679 @@
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
+ """
15
+ Loader class for files created by saving BBCI files in matlab (make
16
+ sure to save with '-v7.3' in matlab, see
17
+ https://de.mathworks.com/help/matlab/import_export/mat-file-versions.html#buk6i87
18
+ )
19
+
20
+ Parameters
21
+ ----------
22
+ filename: str
23
+ load_sensor_names: list of str, optional
24
+ Also speeds up loading if you only load some sensors.
25
+ None means load all sensors.
26
+ check_class_names: bool, optional
27
+ check if the class names are part of some known class names at
28
+ Translational NeuroTechnology Lab, AG Ball, Freiburg, Germany.
29
+ """
30
+
31
+ def __init__(
32
+ self, filename, load_sensor_names=None, check_class_names=False
33
+ ):
34
+ self.__dict__.update(locals())
35
+ del self.self
36
+
37
+ def load(self):
38
+ cnt = self._load_continuous_signal()
39
+ cnt = self._add_markers(cnt)
40
+ return cnt
41
+
42
+ def _load_continuous_signal(self):
43
+ wanted_chan_inds, wanted_sensor_names = self._determine_sensors()
44
+ fs = self._determine_samplingrate()
45
+ with h5py.File(self.filename, "r") as h5file:
46
+ samples = int(h5file["nfo"]["T"][0, 0])
47
+ cnt_signal_shape = (samples, len(wanted_chan_inds))
48
+ continuous_signal = (
49
+ np.ones(cnt_signal_shape, dtype=np.float32) * np.nan
50
+ )
51
+ for chan_ind_arr, chan_ind_set in enumerate(wanted_chan_inds):
52
+ # + 1 because matlab/this hdf5-naming logic
53
+ # has 1-based indexing
54
+ # i.e ch1,ch2,....
55
+ chan_set_name = "ch" + str(chan_ind_set + 1)
56
+ # first 0 to unpack into vector, before it is 1xN matrix
57
+ chan_signal = h5file[chan_set_name][
58
+ :
59
+ ].squeeze() # already load into memory
60
+ continuous_signal[:, chan_ind_arr] = chan_signal
61
+ assert not np.any(
62
+ np.isnan(continuous_signal)
63
+ ), "No NaNs expected in signal"
64
+
65
+ if self.load_sensor_names is None:
66
+ ch_types = ["EEG"] * len(wanted_chan_inds)
67
+ else:
68
+ # Assume we cant know channel type here automatically
69
+ ch_types = ["misc"] * len(wanted_chan_inds)
70
+ info = mne.create_info(
71
+ ch_names=wanted_sensor_names, sfreq=fs, ch_types=ch_types
72
+ )
73
+
74
+ cnt = mne.io.RawArray(continuous_signal.T, info)
75
+ return cnt
76
+
77
+ def _determine_sensors(self):
78
+ all_sensor_names = self.get_all_sensors(self.filename, pattern=None)
79
+ if self.load_sensor_names is None:
80
+
81
+ # if no sensor names given, take all EEG-chans
82
+ eeg_sensor_names = all_sensor_names
83
+ eeg_sensor_names = filter(
84
+ lambda s: not s.startswith("BIP"), eeg_sensor_names
85
+ )
86
+ eeg_sensor_names = filter(
87
+ lambda s: not s.startswith("E"), eeg_sensor_names
88
+ )
89
+ eeg_sensor_names = filter(
90
+ lambda s: not s.startswith("Microphone"), eeg_sensor_names
91
+ )
92
+ eeg_sensor_names = filter(
93
+ lambda s: not s.startswith("Breath"), eeg_sensor_names
94
+ )
95
+ eeg_sensor_names = filter(
96
+ lambda s: not s.startswith("GSR"), eeg_sensor_names
97
+ )
98
+ eeg_sensor_names = list(eeg_sensor_names)
99
+ assert (
100
+ len(eeg_sensor_names) == 128
101
+ or len(eeg_sensor_names) == 64
102
+ or len(eeg_sensor_names) == 32
103
+ or len(eeg_sensor_names) == 16
104
+ ), "Recheck this code if you have different sensors..."
105
+ self.load_sensor_names = eeg_sensor_names
106
+ chan_inds = self._determine_chan_inds(
107
+ all_sensor_names, self.load_sensor_names
108
+ )
109
+ return chan_inds, self.load_sensor_names
110
+
111
+ def _determine_samplingrate(self):
112
+ with h5py.File(self.filename, "r") as h5file:
113
+ fs = h5file["nfo"]["fs"][0, 0]
114
+ assert isinstance(fs, int) or fs.is_integer()
115
+ fs = int(fs)
116
+ return fs
117
+
118
+ @staticmethod
119
+ def _determine_chan_inds(all_sensor_names, sensor_names):
120
+ assert sensor_names is not None
121
+ chan_inds = [all_sensor_names.index(s) for s in sensor_names]
122
+ assert len(chan_inds) == len(sensor_names), (
123
+ "All" "sensors should be there."
124
+ )
125
+ assert len(set(chan_inds)) == len(chan_inds), (
126
+ "No" "duplicated sensors wanted."
127
+ )
128
+ return chan_inds
129
+
130
+ @staticmethod
131
+ def get_all_sensors(filename, pattern=None):
132
+ """
133
+ Get all sensors that exist in the given file.
134
+
135
+ Parameters
136
+ ----------
137
+ filename: str
138
+ pattern: str, optional
139
+ Only return those sensor names that match the given pattern.
140
+
141
+ Returns
142
+ -------
143
+ sensor_names: list of str
144
+ Sensor names that match the pattern or all sensor names in the file.
145
+ """
146
+ with h5py.File(filename, "r") as h5file:
147
+ clab_set = h5file["nfo"]["clab"][:].squeeze()
148
+ all_sensor_names = [
149
+ "".join(chr(c.item()) for c in h5file[obj_ref]) for obj_ref in clab_set
150
+ ]
151
+ if pattern is not None:
152
+ all_sensor_names = filter(
153
+ lambda sname: re.search(pattern, sname), all_sensor_names
154
+ )
155
+ return all_sensor_names
156
+
157
+ def _add_markers(self, cnt):
158
+ with h5py.File(self.filename, "r") as h5file:
159
+ event_times_in_ms = h5file["mrk"]["time"][:].squeeze()
160
+ event_classes = (
161
+ h5file["mrk"]["event"]["desc"][:].squeeze().astype(np.int64)
162
+ )
163
+
164
+ # Check whether class names known and correct order
165
+ class_name_set = h5file["nfo"]["className"][:].squeeze()
166
+ all_class_names = [
167
+ "".join(chr(c.item()) for c in h5file[obj_ref])
168
+ for obj_ref in class_name_set
169
+ ]
170
+
171
+ if self.check_class_names:
172
+ _check_class_names(
173
+ all_class_names, event_times_in_ms, event_classes
174
+ )
175
+
176
+ event_times_in_samples = event_times_in_ms * cnt.info["sfreq"] / 1000.0
177
+ event_times_in_samples = np.uint32(np.round(event_times_in_samples))
178
+
179
+ # Check if there are markers at the same time
180
+ previous_i_sample = -1
181
+ for i_event, (i_sample, id_class) in enumerate(
182
+ zip(event_times_in_samples, event_classes)
183
+ ):
184
+ if i_sample == previous_i_sample:
185
+ log.warning(
186
+ "Same sample has at least two markers.\n"
187
+ "{:d}: ({:.0f} and {:.0f}).\n".format(
188
+ i_sample,
189
+ event_classes[i_event - 1],
190
+ event_classes[i_event],
191
+ )
192
+ + "Marker codes will be summed."
193
+ )
194
+ previous_i_sample = i_sample
195
+
196
+ # Now create stim chan
197
+ stim_chan = np.zeros_like(cnt.get_data()[0])
198
+ for i_sample, id_class in zip(event_times_in_samples, event_classes):
199
+ stim_chan[i_sample] += id_class
200
+ info = mne.create_info(
201
+ ch_names=["STI 014"], sfreq=cnt.info["sfreq"], ch_types=["stim"]
202
+ )
203
+ stim_cnt = mne.io.RawArray(stim_chan[None], info, verbose="WARNING")
204
+ cnt = cnt.add_channels([stim_cnt])
205
+ event_arr = [
206
+ event_times_in_samples,
207
+ [0] * len(event_times_in_samples),
208
+ event_classes,
209
+ ]
210
+ cnt.info["events"] = np.array(event_arr).T
211
+
212
+ event_times_in_sec = event_times_in_ms / 1000.0
213
+ # 4 second trials
214
+ durations = np.full(event_times_in_ms.shape, 4)
215
+ # Label information for this dataset
216
+ # have to add 1 as class labels start from 1, not 0 (due to matlab)
217
+ event_desc = dict([(i+1, c) for i,c in enumerate(all_class_names)])
218
+ descriptions = [event_desc[y] for y in event_classes]
219
+ annots = mne.Annotations(event_times_in_sec, durations, descriptions)
220
+ cnt.set_annotations(annots)
221
+
222
+ return cnt
223
+
224
+
225
+ def _check_class_names(all_class_names, event_times_in_ms, event_classes):
226
+ """
227
+ Checks if the class names are part of some known class names used in
228
+ translational neurotechnology lab, AG Ball, Freiburg.
229
+
230
+ Logs warning in case class names are not known.
231
+
232
+ Parameters
233
+ ----------
234
+ all_class_names: list of str
235
+ event_times_in_ms: list of number
236
+ event_classes: list of number
237
+
238
+ """
239
+ if all_class_names == ["Right Hand", "Left Hand", "Rest", "Feet"]:
240
+ pass
241
+ elif (
242
+ (
243
+ all_class_names
244
+ == [
245
+ "1",
246
+ "10",
247
+ "11",
248
+ "111",
249
+ "12",
250
+ "13",
251
+ "150",
252
+ "2",
253
+ "20",
254
+ "22",
255
+ "3",
256
+ "30",
257
+ "33",
258
+ "4",
259
+ "40",
260
+ "44",
261
+ "99",
262
+ ]
263
+ )
264
+ or (
265
+ all_class_names
266
+ == [
267
+ "1",
268
+ "10",
269
+ "11",
270
+ "12",
271
+ "13",
272
+ "150",
273
+ "2",
274
+ "20",
275
+ "22",
276
+ "3",
277
+ "30",
278
+ "33",
279
+ "4",
280
+ "40",
281
+ "44",
282
+ "99",
283
+ ]
284
+ )
285
+ or (all_class_names == ["1", "2", "3", "4"])
286
+ ):
287
+ pass # Semantic classes
288
+ elif all_class_names == ["Rest", "Feet", "Left Hand", "Right Hand"]:
289
+ # Have to swap from
290
+ # ['Rest', 'Feet', 'Left Hand', 'Right Hand']
291
+ # to
292
+ # ['Right Hand', 'Left Hand', 'Rest', 'Feet']
293
+ right_mask = event_classes == 4
294
+ left_mask = event_classes == 3
295
+ rest_mask = event_classes == 1
296
+ feet_mask = event_classes == 2
297
+ event_classes[right_mask] = 1
298
+ event_classes[left_mask] = 2
299
+ event_classes[rest_mask] = 3
300
+ event_classes[feet_mask] = 4
301
+ log.warn(
302
+ "Swapped class names {:s}... might cause problems...".format(
303
+ all_class_names
304
+ )
305
+ )
306
+ elif all_class_names == [
307
+ "Right Hand Start",
308
+ "Left Hand Start",
309
+ "Rest Start",
310
+ "Feet Start",
311
+ "Right Hand End",
312
+ "Left Hand End",
313
+ "Rest End",
314
+ "Feet End",
315
+ ]:
316
+ pass
317
+ elif all_class_names == [
318
+ "Right Hand",
319
+ "Left Hand",
320
+ "Rest",
321
+ "Feet",
322
+ "Face",
323
+ "Navigation",
324
+ "Music",
325
+ "Rotation",
326
+ "Subtraction",
327
+ "Words",
328
+ ]:
329
+ pass # robot hall 10 class decoding
330
+ elif all_class_names == [
331
+ "RightHand",
332
+ "Feet",
333
+ "Rotation",
334
+ "Words",
335
+ "\x00\x00",
336
+ "\x00\x00",
337
+ "\x00\x00",
338
+ "\x00\x00",
339
+ "\x00\x00",
340
+ "RightHand_End",
341
+ "\x00\x00",
342
+ "\x00\x00",
343
+ "\x00\x00",
344
+ "\x00\x00",
345
+ "\x00\x00",
346
+ "\x00\x00",
347
+ "\x00\x00",
348
+ "\x00\x00",
349
+ "\x00\x00",
350
+ "Feet_End",
351
+ "\x00\x00",
352
+ "\x00\x00",
353
+ "\x00\x00",
354
+ "\x00\x00",
355
+ "\x00\x00",
356
+ "\x00\x00",
357
+ "\x00\x00",
358
+ "\x00\x00",
359
+ "\x00\x00",
360
+ "Rotation_End",
361
+ "\x00\x00",
362
+ "\x00\x00",
363
+ "\x00\x00",
364
+ "\x00\x00",
365
+ "\x00\x00",
366
+ "\x00\x00",
367
+ "\x00\x00",
368
+ "\x00\x00",
369
+ "\x00\x00",
370
+ "Words_End",
371
+ ] or all_class_names == [
372
+ "RightHand",
373
+ "Feet",
374
+ "Rotation",
375
+ "Words",
376
+ "Rest",
377
+ "\x00\x00",
378
+ "\x00\x00",
379
+ "\x00\x00",
380
+ "\x00\x00",
381
+ "RightHand_End",
382
+ "\x00\x00",
383
+ "\x00\x00",
384
+ "\x00\x00",
385
+ "\x00\x00",
386
+ "\x00\x00",
387
+ "\x00\x00",
388
+ "\x00\x00",
389
+ "\x00\x00",
390
+ "\x00\x00",
391
+ "Feet_End",
392
+ "\x00\x00",
393
+ "\x00\x00",
394
+ "\x00\x00",
395
+ "\x00\x00",
396
+ "\x00\x00",
397
+ "\x00\x00",
398
+ "\x00\x00",
399
+ "\x00\x00",
400
+ "\x00\x00",
401
+ "Rotation_End",
402
+ "\x00\x00",
403
+ "\x00\x00",
404
+ "\x00\x00",
405
+ "\x00\x00",
406
+ "\x00\x00",
407
+ "\x00\x00",
408
+ "\x00\x00",
409
+ "\x00\x00",
410
+ "\x00\x00",
411
+ "Words_End",
412
+ "\x00\x00",
413
+ "\x00\x00",
414
+ "\x00\x00",
415
+ "\x00\x00",
416
+ "\x00\x00",
417
+ "\x00\x00",
418
+ "\x00\x00",
419
+ "\x00\x00",
420
+ "\x00\x00",
421
+ "Rest_End",
422
+ ]:
423
+ pass # weird stuff when we recorded cursor in robot hall
424
+ # on 2016-09-14 and 2016-09-16 :D
425
+
426
+ elif all_class_names == [
427
+ "0004",
428
+ "0016",
429
+ "0032",
430
+ "0056",
431
+ "0064",
432
+ "0088",
433
+ "0095",
434
+ "0120",
435
+ ]:
436
+ pass
437
+ elif all_class_names == ["0004", "0056", "0088", "0120"]:
438
+ pass
439
+ elif all_class_names == [
440
+ "0004",
441
+ "0016",
442
+ "0032",
443
+ "0048",
444
+ "0056",
445
+ "0064",
446
+ "0080",
447
+ "0088",
448
+ "0095",
449
+ "0120",
450
+ ]:
451
+ pass
452
+ elif all_class_names == ["0004", "0016", "0056", "0088", "0120", "__"]:
453
+ pass
454
+ elif all_class_names == ["0004", "0056", "0088", "0120", "__"]:
455
+ pass
456
+ elif all_class_names == [
457
+ "0004",
458
+ "0032",
459
+ "0048",
460
+ "0056",
461
+ "0064",
462
+ "0080",
463
+ "0088",
464
+ "0095",
465
+ "0120",
466
+ "__",
467
+ ]:
468
+ pass
469
+ elif all_class_names == [
470
+ "0004",
471
+ "0056",
472
+ "0080",
473
+ "0088",
474
+ "0096",
475
+ "0120",
476
+ "__",
477
+ ]:
478
+ pass
479
+ elif all_class_names == [
480
+ "0004",
481
+ "0032",
482
+ "0056",
483
+ "0064",
484
+ "0080",
485
+ "0088",
486
+ "0095",
487
+ "0120",
488
+ ]:
489
+ pass
490
+ elif all_class_names == [
491
+ "0004",
492
+ "0032",
493
+ "0048",
494
+ "0056",
495
+ "0064",
496
+ "0080",
497
+ "0088",
498
+ "0095",
499
+ "0120",
500
+ ]:
501
+ pass
502
+ elif all_class_names == [
503
+ "0004",
504
+ "0016",
505
+ "0032",
506
+ "0048",
507
+ "0056",
508
+ "0064",
509
+ "0080",
510
+ "0088",
511
+ "0095",
512
+ "0096",
513
+ "0120",
514
+ ]:
515
+ pass
516
+ elif all_class_names == ["4", "16", "32", "56", "64", "88", "95", "120"]:
517
+ pass
518
+ elif all_class_names == ["4", "56", "88", "120"]:
519
+ pass
520
+ elif all_class_names == [
521
+ "4",
522
+ "16",
523
+ "32",
524
+ "48",
525
+ "56",
526
+ "64",
527
+ "80",
528
+ "88",
529
+ "95",
530
+ "120",
531
+ ]:
532
+ pass
533
+ elif all_class_names == ["0", "4", "56", "88", "120"]:
534
+ pass
535
+ elif all_class_names == ["0", "4", "16", "56", "88", "120"]:
536
+ pass
537
+ elif all_class_names == [
538
+ "0",
539
+ "4",
540
+ "32",
541
+ "48",
542
+ "56",
543
+ "64",
544
+ "80",
545
+ "88",
546
+ "95",
547
+ "120",
548
+ ]:
549
+ pass
550
+ elif all_class_names == ["0", "4", "56", "80", "88", "96", "120"]:
551
+ pass
552
+ elif all_class_names == ["4", "32", "56", "64", "80", "88", "95", "120"]:
553
+ pass
554
+ elif all_class_names == ["One", "Two", "Three", "Four"]:
555
+ pass
556
+ elif all_class_names == [
557
+ "1",
558
+ "10",
559
+ "11",
560
+ "12",
561
+ "2",
562
+ "20",
563
+ "3",
564
+ "30",
565
+ "4",
566
+ "40",
567
+ ]:
568
+ pass
569
+ elif all_class_names == [
570
+ "1",
571
+ "10",
572
+ "12",
573
+ "13",
574
+ "2",
575
+ "20",
576
+ "3",
577
+ "30",
578
+ "4",
579
+ "40",
580
+ ]:
581
+ pass
582
+ elif all_class_names == [
583
+ "1",
584
+ "10",
585
+ "13",
586
+ "2",
587
+ "20",
588
+ "3",
589
+ "30",
590
+ "4",
591
+ "40",
592
+ "99",
593
+ ]:
594
+ pass
595
+ elif all_class_names == [
596
+ "1",
597
+ "10",
598
+ "11",
599
+ "14",
600
+ "18",
601
+ "20",
602
+ "21",
603
+ "24",
604
+ "251",
605
+ "252",
606
+ "28",
607
+ "30",
608
+ "4",
609
+ "8",
610
+ ]:
611
+ pass
612
+ elif all_class_names == [
613
+ "1",
614
+ "10",
615
+ "11",
616
+ "14",
617
+ "18",
618
+ "20",
619
+ "21",
620
+ "24",
621
+ "252",
622
+ "253",
623
+ "28",
624
+ "30",
625
+ "4",
626
+ "8",
627
+ ]:
628
+ pass
629
+ elif len(event_times_in_ms) == len(all_class_names):
630
+ pass # weird neuroone(?) logic where class names have event classes
631
+ elif all_class_names == [
632
+ "Right_hand_stimulus_onset",
633
+ "Feet_stimulus_onset",
634
+ "Rotation_stimulus_onset",
635
+ "Words_stimulus_onset",
636
+ "Right_hand_stimulus_offset",
637
+ "Feet_stimulus_offset",
638
+ "Rotation_stimulus_offset",
639
+ "Words_stimulus_offset",
640
+ ]:
641
+ pass
642
+ else:
643
+ # remove this whole if else stuffs?
644
+ log.warn("Unknown class names {:s}".format(all_class_names))
645
+
646
+
647
+ def load_bbci_sets_from_folder(folder, runs="all"):
648
+ """
649
+ Load bbci datasets from files in given folder.
650
+
651
+ Parameters
652
+ ----------
653
+ folder: str
654
+ Folder with .BBCI.mat files inside
655
+ runs: list of int
656
+ If you only want to load specific runs.
657
+ Assumes filenames with such kind of part: S001R02 for Run 2.
658
+ Tries to match this regex: ``'S[0-9]{3,3}R[0-9]{2,2}_'``.
659
+
660
+ Returns
661
+ -------
662
+
663
+ """
664
+ bbci_mat_files = sorted(glob(os.path.join(folder, "*.BBCI.mat")))
665
+ if runs != "all":
666
+ file_run_numbers = [
667
+ int(re.search("S[0-9]{3,3}R[0-9]{2,2}_", f).group()[5:7])
668
+ for f in bbci_mat_files
669
+ ]
670
+ indices = [file_run_numbers.index(num) for num in runs]
671
+
672
+ wanted_files = np.array(bbci_mat_files)[indices]
673
+ else:
674
+ wanted_files = bbci_mat_files
675
+ cnts = []
676
+ for f in wanted_files:
677
+ log.info("Loading {:s}".format(f))
678
+ cnts.append(BBCIDataset(f).load())
679
+ return cnts