braindecode 0.2.1__tar.gz → 0.3.0__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 (74) hide show
  1. {Braindecode-0.2.1 → Braindecode-0.3.0}/Braindecode.egg-info/PKG-INFO +1 -1
  2. {Braindecode-0.2.1 → Braindecode-0.3.0}/PKG-INFO +1 -1
  3. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datasets/bcic_iv_2a.py +2 -2
  4. Braindecode-0.3.0/braindecode/datautil/trial_segment.py +628 -0
  5. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/experiments/monitors.py +37 -3
  6. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/models/shallow_fbcsp.py +17 -11
  7. Braindecode-0.3.0/braindecode/version.py +1 -0
  8. Braindecode-0.3.0/docs/notebooks/BBCI_Data.ipynb +886 -0
  9. Braindecode-0.3.0/docs/notebooks/BBCI_Data_Start_Stop.ipynb +899 -0
  10. Braindecode-0.3.0/docs/notebooks/BBCI_Data_Trialwise.ipynb +761 -0
  11. Braindecode-0.3.0/docs/notebooks/Experiment_Class.ipynb +804 -0
  12. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/notebooks/TrialWise_Decoding.ipynb +12 -12
  13. Braindecode-0.3.0/docs/notebooks/visualization/Perturbation.ipynb +759 -0
  14. Braindecode-0.2.1/braindecode/datautil/trial_segment.py +0 -312
  15. Braindecode-0.2.1/braindecode/version.py +0 -1
  16. Braindecode-0.2.1/docs/notebooks/BBCI_Data.ipynb +0 -845
  17. Braindecode-0.2.1/docs/notebooks/BBCI_Data_Start_Stop.ipynb +0 -802
  18. Braindecode-0.2.1/docs/notebooks/BBCI_Data_Trialwise.ipynb +0 -839
  19. Braindecode-0.2.1/docs/notebooks/Experiment_Class.ipynb +0 -804
  20. Braindecode-0.2.1/docs/notebooks/visualization/Perturbation.ipynb +0 -753
  21. {Braindecode-0.2.1 → Braindecode-0.3.0}/Braindecode.egg-info/SOURCES.txt +0 -0
  22. {Braindecode-0.2.1 → Braindecode-0.3.0}/Braindecode.egg-info/dependency_links.txt +0 -0
  23. {Braindecode-0.2.1 → Braindecode-0.3.0}/Braindecode.egg-info/not-zip-safe +0 -0
  24. {Braindecode-0.2.1 → Braindecode-0.3.0}/Braindecode.egg-info/requires.txt +0 -0
  25. {Braindecode-0.2.1 → Braindecode-0.3.0}/Braindecode.egg-info/top_level.txt +0 -0
  26. {Braindecode-0.2.1 → Braindecode-0.3.0}/LICENSE.txt +0 -0
  27. {Braindecode-0.2.1 → Braindecode-0.3.0}/MANIFEST.in +0 -0
  28. {Braindecode-0.2.1 → Braindecode-0.3.0}/README.rst +0 -0
  29. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/__init__.py +0 -0
  30. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datasets/__init__.py +0 -0
  31. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datasets/bbci.py +0 -0
  32. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datasets/multiple.py +0 -0
  33. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datasets/sensor_positions.py +0 -0
  34. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datautil/__init__.py +0 -0
  35. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datautil/iterators.py +0 -0
  36. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datautil/signal_target.py +0 -0
  37. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datautil/signalproc.py +0 -0
  38. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datautil/splitters.py +0 -0
  39. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/datautil/util.py +0 -0
  40. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/experiments/__init__.py +0 -0
  41. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/experiments/experiment.py +0 -0
  42. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/experiments/stopcriteria.py +0 -0
  43. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/mne_ext/__init__.py +0 -0
  44. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/mne_ext/signalproc.py +0 -0
  45. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/models/__init__.py +0 -0
  46. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/models/deep4.py +0 -0
  47. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/models/eegnet.py +0 -0
  48. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/models/util.py +0 -0
  49. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/torch_ext/__init__.py +0 -0
  50. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/torch_ext/constraints.py +0 -0
  51. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/torch_ext/functions.py +0 -0
  52. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/torch_ext/init.py +0 -0
  53. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/torch_ext/losses.py +0 -0
  54. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/torch_ext/modules.py +0 -0
  55. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/torch_ext/util.py +0 -0
  56. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/util.py +0 -0
  57. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/visualization/__init__.py +0 -0
  58. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/visualization/perturbation.py +0 -0
  59. {Braindecode-0.2.1 → Braindecode-0.3.0}/braindecode/visualization/plot.py +0 -0
  60. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/Makefile +0 -0
  61. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/conf.py +0 -0
  62. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/index.rst +0 -0
  63. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/notebooks/Cropped_Decoding.ipynb +0 -0
  64. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.datasets.rst +0 -0
  65. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.datautil.rst +0 -0
  66. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.experiments.rst +0 -0
  67. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.mne_ext.rst +0 -0
  68. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.models.rst +0 -0
  69. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.rst +0 -0
  70. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.torch_ext.rst +0 -0
  71. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/braindecode.visualization.rst +0 -0
  72. {Braindecode-0.2.1 → Braindecode-0.3.0}/docs/source/modules.rst +0 -0
  73. {Braindecode-0.2.1 → Braindecode-0.3.0}/setup.cfg +0 -0
  74. {Braindecode-0.2.1 → Braindecode-0.3.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: Braindecode
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: Braindecode
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
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
@@ -33,7 +33,7 @@ class BCICompetition4Set2A(object):
33
33
  mask = np.isnan(data[i_chan])
34
34
  chan_mean = np.nanmean(data[i_chan])
35
35
  data[i_chan, mask] = chan_mean
36
- gdf_events = raw_edf.get_edf_events()
36
+ gdf_events = raw_edf.find_edf_events()
37
37
  raw_edf = mne.io.RawArray(data, raw_edf.info, verbose='WARNING')
38
38
  # remember gdf events
39
39
  raw_edf.info['gdf_events'] = gdf_events
@@ -76,4 +76,4 @@ class BCICompetition4Set2A(object):
76
76
  events[:,0] = trial_events[:,0]
77
77
  events[:,2] = trial_events[:,1]
78
78
 
79
- return events, artifact_trial_mask
79
+ return events, artifact_trial_mask
@@ -0,0 +1,628 @@
1
+ import logging
2
+ from collections import OrderedDict, Counter
3
+ from copy import deepcopy
4
+
5
+ import numpy as np
6
+
7
+ from braindecode.datautil.signal_target import SignalAndTarget
8
+ from braindecode.datautil.util import ms_to_samples
9
+
10
+ log = logging.getLogger(__name__)
11
+
12
+ marker_def = OrderedDict(
13
+ (['Right', 1], ['Left', [2]], ['Rest', 3], ['Feet', 4]))
14
+
15
+
16
+ def create_signal_target_from_raw_mne(
17
+ raw, name_to_start_codes, epoch_ival_ms,
18
+ name_to_stop_codes=None,
19
+ prepad_trials_to_n_samples=None,
20
+ one_hot_labels=False,
21
+ one_label_per_trial=True):
22
+ """
23
+ Create SignalTarget set from given `mne.io.RawArray`.
24
+
25
+ Parameters
26
+ ----------
27
+ raw: `mne.io.RawArray`
28
+ name_to_start_codes: OrderedDict (str -> int or list of int)
29
+ Ordered dictionary mapping class names to marker code or marker codes.
30
+ y-labels will be assigned in increasing key order, i.e.
31
+ first classname gets y-value 0, second classname y-value 1, etc.
32
+ epoch_ival_ms: iterable of (int,int)
33
+ Epoching interval in milliseconds. In case only `name_to_codes` given,
34
+ represents start offset and stop offset from start markers. In case
35
+ `name_to_stop_codes` given, represents offset from start marker
36
+ and offset from stop marker. E.g. [500, -500] would mean 500ms
37
+ after the start marker until 500 ms before the stop marker.
38
+ name_to_stop_codes: dict (str -> int or list of int), optional
39
+ Dictionary mapping class names to stop marker code or stop marker codes.
40
+ Order does not matter, dictionary should contain each class in
41
+ `name_to_codes` dictionary.
42
+ prepad_trials_to_n_samples: int
43
+ Pad trials that would be too short with the signal before it (only
44
+ valid if name_to_stop_codes is not None).
45
+ one_hot_labels: bool, optional
46
+ Whether to have the labels in a one-hot format, e.g. [0,0,1] or to
47
+ have them just as an int, e.g. 2
48
+ one_label_per_trial: bool, optional
49
+ Whether to have a timeseries of labels or just a single label per trial.
50
+
51
+ Returns
52
+ -------
53
+ dataset: :class:`.SignalAndTarget`
54
+ Dataset with `X` as the trial signals and `y` as the trial labels.
55
+ """
56
+ data = raw.get_data()
57
+ events = np.array([raw.info['events'][:,0],
58
+ raw.info['events'][:,2]]).T
59
+ fs = raw.info['sfreq']
60
+ return create_signal_target(
61
+ data, events, fs, name_to_start_codes,
62
+ epoch_ival_ms,
63
+ name_to_stop_codes=name_to_stop_codes,
64
+ prepad_trials_to_n_samples=prepad_trials_to_n_samples,
65
+ one_hot_labels=one_hot_labels,
66
+ one_label_per_trial=one_label_per_trial)
67
+
68
+
69
+ def create_signal_target(data, events, fs, name_to_start_codes, epoch_ival_ms,
70
+ name_to_stop_codes=None, prepad_trials_to_n_samples=None,
71
+ one_hot_labels=False, one_label_per_trial=True):
72
+ """
73
+ Create SignalTarget set given continuous data.
74
+
75
+ Parameters
76
+ ----------
77
+ data: 2d-array of number
78
+ The continuous recorded data. Channels x times order.
79
+ events: 2d-array
80
+ Dimensions: Number of events, 2. For each event, should contain sample
81
+ index and marker code.
82
+ fs: number
83
+ Sampling rate.
84
+ name_to_start_codes: OrderedDict (str -> int or list of int)
85
+ Ordered dictionary mapping class names to marker code or marker codes.
86
+ y-labels will be assigned in increasing key order, i.e.
87
+ first classname gets y-value 0, second classname y-value 1, etc.
88
+ epoch_ival_ms: iterable of (int,int)
89
+ Epoching interval in milliseconds. In case only `name_to_codes` given,
90
+ represents start offset and stop offset from start markers. In case
91
+ `name_to_stop_codes` given, represents offset from start marker
92
+ and offset from stop marker. E.g. [500, -500] would mean 500ms
93
+ after the start marker until 500 ms before the stop marker.
94
+ name_to_stop_codes: dict (str -> int or list of int), optional
95
+ Dictionary mapping class names to stop marker code or stop marker codes.
96
+ Order does not matter, dictionary should contain each class in
97
+ `name_to_codes` dictionary.
98
+ prepad_trials_to_n_samples: int, optional
99
+ Pad trials that would be too short with the signal before it (only
100
+ valid if name_to_stop_codes is not None).
101
+ one_hot_labels: bool, optional
102
+ Whether to have the labels in a one-hot format, e.g. [0,0,1] or to
103
+ have them just as an int, e.g. 2
104
+ one_label_per_trial: bool, optional
105
+ Whether to have a timeseries of labels or just a single label per trial.
106
+
107
+ Returns
108
+ -------
109
+ dataset: :class:`.SignalAndTarget`
110
+ Dataset with `X` as the trial signals and `y` as the trial labels.
111
+
112
+ """
113
+ if name_to_stop_codes is None:
114
+ return _create_signal_target_from_start_and_ival(
115
+ data, events, fs, name_to_start_codes, epoch_ival_ms,
116
+ one_hot_labels=one_hot_labels,
117
+ one_label_per_trial=one_label_per_trial)
118
+ else:
119
+ return _create_signal_target_from_start_and_stop(
120
+ data, events, fs, name_to_start_codes, epoch_ival_ms,
121
+ name_to_stop_codes, prepad_trials_to_n_samples,
122
+ one_hot_labels=one_hot_labels,
123
+ one_label_per_trial=one_label_per_trial)
124
+
125
+
126
+ def _to_mrk_code_to_name_and_y(name_to_codes):
127
+ # Create mapping from marker code to class name and y=classindex
128
+ mrk_code_to_name_and_y = {}
129
+ for i_class, class_name in enumerate(name_to_codes):
130
+ codes = name_to_codes[class_name]
131
+ if hasattr(codes, '__len__'):
132
+ for code in codes:
133
+ assert code not in mrk_code_to_name_and_y
134
+ mrk_code_to_name_and_y[code] = (class_name, i_class)
135
+ else:
136
+ assert codes not in mrk_code_to_name_and_y
137
+ mrk_code_to_name_and_y[codes] = (class_name, i_class)
138
+ return mrk_code_to_name_and_y
139
+
140
+
141
+ def _create_signal_target_from_start_and_ival(
142
+ data, events, fs, name_to_codes, epoch_ival_ms,
143
+ one_hot_labels, one_label_per_trial):
144
+ cnt_y, i_start_stops = _create_cnt_y_and_trial_bounds_from_start_and_ival(
145
+ data.shape[1], events, fs, name_to_codes, epoch_ival_ms
146
+ )
147
+ signal_target = _create_signal_target_from_cnt_y_start_stops(
148
+ data, cnt_y, i_start_stops, prepad_trials_to_n_samples=None,
149
+ one_hot_labels=one_hot_labels,
150
+ one_label_per_trial=one_label_per_trial)
151
+ # make into arrray as all should have same dimensions
152
+ signal_target.X = np.array(signal_target.X, dtype=np.float32)
153
+ signal_target.y = np.array(signal_target.y, dtype=np.int64)
154
+ return signal_target
155
+
156
+
157
+ def _create_cnt_y_and_trial_bounds_from_start_and_ival(
158
+ n_samples, events, fs, name_to_start_codes, epoch_ival_ms):
159
+ ival_in_samples = ms_to_samples(np.array(epoch_ival_ms), fs)
160
+ start_offset = np.int32(np.round(ival_in_samples[0]))
161
+ # we will use ceil but exclusive...
162
+ stop_offset = np.int32(np.ceil(ival_in_samples[1]))
163
+ mrk_code_to_name_and_y = _to_mrk_code_to_name_and_y(name_to_start_codes)
164
+ class_to_n_trials = Counter()
165
+ n_classes = len(name_to_start_codes)
166
+ cnt_y = np.zeros((n_samples, n_classes), dtype=np.int64)
167
+ i_start_stops = []
168
+ for i_sample, mrk_code in zip(events[:, 0], events[:, 1]):
169
+ start_sample = int(i_sample) + start_offset
170
+ stop_sample = int(i_sample) + stop_offset
171
+ if mrk_code in mrk_code_to_name_and_y:
172
+ if start_sample < 0:
173
+ log.warning(
174
+ "Ignore trial with marker code {:d}, would start at "
175
+ "sample {:d}".format(mrk_code, start_sample))
176
+ continue
177
+ if stop_sample > n_samples:
178
+ log.warning("Ignore trial with marker code {:d}, would end at "
179
+ "sample {:d} of {:d}".format(
180
+ mrk_code, stop_sample - 1, n_samples - 1))
181
+ continue
182
+
183
+ name, this_y = mrk_code_to_name_and_y[mrk_code]
184
+ i_start_stops.append((start_sample, stop_sample))
185
+ cnt_y[start_sample:stop_sample, this_y] = 1
186
+ class_to_n_trials[name] += 1
187
+ log.info("Trial per class:\n{:s}".format(str(class_to_n_trials)))
188
+ return cnt_y, i_start_stops
189
+
190
+
191
+ def _create_signal_target_from_start_and_stop(
192
+ data, events, fs, name_to_start_codes, epoch_ival_ms,
193
+ name_to_stop_codes, prepad_trials_to_n_samples,
194
+ one_hot_labels, one_label_per_trial, ):
195
+ assert np.array_equal(list(name_to_start_codes.keys()),
196
+ list(name_to_stop_codes.keys()))
197
+ cnt_y, i_start_stops = _create_cnt_y_and_trial_bounds_from_start_stop(
198
+ data.shape[1],events, fs,name_to_start_codes, epoch_ival_ms,
199
+ name_to_stop_codes)
200
+ signal_target = _create_signal_target_from_cnt_y_start_stops(
201
+ data, cnt_y, i_start_stops,
202
+ prepad_trials_to_n_samples=prepad_trials_to_n_samples,
203
+ one_hot_labels=one_hot_labels, one_label_per_trial=one_label_per_trial)
204
+ return signal_target
205
+
206
+
207
+ def _create_cnt_y_and_trial_bounds_from_start_stop(
208
+ n_samples, events, fs, name_to_start_codes, epoch_ival_ms,
209
+ name_to_stop_codes):
210
+ """
211
+ Create a one-hot-encoded continuous marker array (cnt_y).
212
+
213
+ Parameters
214
+ ----------
215
+ n_samples: int
216
+ Number of samples=timesteps in the recorded data.
217
+ events: 2d-array
218
+ Dimensions: Number of events, 2. For each event, should contain sample
219
+ index and marker code.
220
+ fs: number
221
+ Sampling rate.
222
+ name_to_start_codes: OrderedDict (str -> int or list of int)
223
+ Ordered dictionary mapping class names to marker code or marker codes.
224
+ y-labels will be assigned in increasing key order, i.e.
225
+ first classname gets y-value 0, second classname y-value 1, etc.
226
+ epoch_ival_ms: iterable of (int,int)
227
+ Epoching interval in milliseconds. In case only `name_to_codes` given,
228
+ represents start offset and stop offset from start markers. In case
229
+ `name_to_stop_codes` given, represents offset from start marker
230
+ and offset from stop marker. E.g. [500, -500] would mean 500ms
231
+ after the start marker until 500 ms before the stop marker.
232
+ name_to_stop_codes: dict (str -> int or list of int), optional
233
+ Dictionary mapping class names to stop marker code or stop marker codes.
234
+ Order does not matter, dictionary should contain each class in
235
+ `name_to_codes` dictionary.
236
+
237
+ Returns
238
+ -------
239
+ cnt_y: 2d-array
240
+ Timeseries of one-hot-labels, time x classes.
241
+ trial_bounds: list of (int,int)
242
+ List of (trial_start, trial_stop) tuples.
243
+
244
+
245
+ """
246
+ assert np.array_equal(list(name_to_start_codes.keys()),
247
+ list(name_to_stop_codes.keys()))
248
+ events = np.asarray(events)
249
+ ival_in_samples = ms_to_samples(np.array(epoch_ival_ms), fs)
250
+ start_offset = np.int32(np.round(ival_in_samples[0]))
251
+ # we will use ceil but exclusive...
252
+ stop_offset = np.int32(np.ceil(ival_in_samples[1]))
253
+ start_code_to_name_and_y = _to_mrk_code_to_name_and_y(name_to_start_codes)
254
+ # Ensure all stop marker codes are iterables
255
+ for name in name_to_stop_codes:
256
+ codes = name_to_stop_codes[name]
257
+ if not hasattr(codes, '__len__'):
258
+ name_to_stop_codes[name] = [codes]
259
+ all_stop_codes = np.concatenate(list(name_to_stop_codes.values())
260
+ ).astype(np.int64)
261
+ class_to_n_trials = Counter()
262
+ n_classes = len(name_to_start_codes)
263
+ cnt_y = np.zeros((n_samples, n_classes), dtype=np.int64)
264
+
265
+ event_samples = events[:, 0]
266
+ event_codes = events[:, 1]
267
+ i_start_stops = []
268
+ i_event = 0
269
+ first_start_code_found = False
270
+ while i_event < len(events):
271
+ while i_event < len(events) and (
272
+ event_codes[i_event] not in start_code_to_name_and_y):
273
+ i_event += 1
274
+ if i_event < len(events):
275
+ start_sample = event_samples[i_event]
276
+ start_code = event_codes[i_event]
277
+ start_name = start_code_to_name_and_y[start_code][0]
278
+ start_y = start_code_to_name_and_y[start_code][1]
279
+ i_event += 1
280
+ first_start_code_found = True
281
+ waiting_for_end_code = True
282
+
283
+ while i_event < len(events) and (
284
+ event_codes[i_event] not in all_stop_codes):
285
+ if event_codes[i_event] in start_code_to_name_and_y:
286
+ log.warning(
287
+ "New start marker {:.0f} at {:.0f} samples found, "
288
+ "no end marker for earlier start marker {:.0f} "
289
+ "at {:.0f} samples found.".format(
290
+ event_codes[i_event], event_samples[i_event],
291
+ start_code, start_sample))
292
+ start_sample = event_samples[i_event]
293
+ start_name = start_code_to_name_and_y[start_code][0]
294
+ start_code = event_codes[i_event]
295
+ start_y = start_code_to_name_and_y[start_code][1]
296
+ i_event += 1
297
+ if i_event == len(events):
298
+ if waiting_for_end_code:
299
+ log.warning(("No end marker for start marker code {:.0f} "
300
+ "at sample {:.0f} found.").format(start_code,
301
+ start_sample))
302
+ elif (not first_start_code_found):
303
+ log.warning("No markers found at all.")
304
+ break
305
+ stop_sample = event_samples[i_event]
306
+ stop_code = event_codes[i_event]
307
+ assert stop_code in name_to_stop_codes[start_name]
308
+ i_start = int(start_sample) + start_offset
309
+ i_stop = int(stop_sample) + stop_offset
310
+ cnt_y[i_start:i_stop, start_y] = 1
311
+ i_start_stops.append((i_start, i_stop))
312
+ class_to_n_trials[start_name] += 1
313
+ waiting_for_end_code = False
314
+
315
+ log.info("Trial per class:\n{:s}".format(str(class_to_n_trials)))
316
+ return cnt_y, i_start_stops
317
+
318
+
319
+ def _create_signal_target_from_cnt_y_start_stops(
320
+ data,
321
+ cnt_y,
322
+ i_start_stops,
323
+ prepad_trials_to_n_samples,
324
+ one_hot_labels,
325
+ one_label_per_trial):
326
+ if prepad_trials_to_n_samples is not None:
327
+ new_i_start_stops = []
328
+ for i_start, i_stop in i_start_stops:
329
+ if (i_stop - i_start) > prepad_trials_to_n_samples:
330
+ new_i_start_stops.append((i_start, i_stop))
331
+ elif i_stop >= prepad_trials_to_n_samples:
332
+ new_i_start_stops.append(
333
+ (i_stop - prepad_trials_to_n_samples, i_stop))
334
+ else:
335
+ log.warning("Could not pad trial enough, therefore not "
336
+ "not using trial from {:d} to {:d}".format(
337
+ i_start, i_stop
338
+ ))
339
+ continue
340
+
341
+ else:
342
+ new_i_start_stops = i_start_stops
343
+
344
+ X = []
345
+ y = []
346
+ for i_start, i_stop in new_i_start_stops:
347
+ if i_start < 0:
348
+ log.warning("Trial start too early, therefore not "
349
+ "not using trial from {:d} to {:d}".format(
350
+ i_start, i_stop
351
+ ))
352
+ continue
353
+ if i_stop > data.shape[1]:
354
+ log.warning("Trial stop too late (past {:d}), therefore not "
355
+ "not using trial from {:d} to {:d}".format(
356
+ data.shape[1] - 1,
357
+ i_start, i_stop
358
+ ))
359
+ continue
360
+ X.append(data[:, i_start:i_stop].astype(np.float32))
361
+ y.append(cnt_y[i_start:i_stop])
362
+
363
+ # take last label always
364
+ if one_label_per_trial:
365
+ new_y = []
366
+ for this_y in y:
367
+ # if destroying one hot later, just set most occuring class to 1
368
+ unique_labels, counts = np.unique(
369
+ this_y, axis=0, return_counts=True)
370
+ if not one_hot_labels:
371
+ meaned_y = np.mean(this_y, axis=0)
372
+ this_new_y = np.zeros_like(meaned_y)
373
+ this_new_y[np.argmax(meaned_y)] = 1
374
+ else:
375
+ # take most frequency occurring label combination
376
+ this_new_y = unique_labels[np.argmax(counts)]
377
+
378
+ if len(unique_labels) > 1:
379
+ log.warning("Different labels within one trial: {:s},"
380
+ "setting single trial label to {:s}".format(
381
+ str(unique_labels), str(this_new_y)
382
+ ))
383
+ new_y.append(this_new_y)
384
+ y = new_y
385
+ if not one_hot_labels:
386
+ # change from one-hot-encoding to regular encoding
387
+ # with -1 as indication none of the classes are present
388
+ new_y = []
389
+ for this_y in y:
390
+ if one_label_per_trial:
391
+ if np.sum(this_y) == 0:
392
+ this_new_y = -1
393
+ else:
394
+ this_new_y = np.argmax(this_y)
395
+ if np.sum(this_y) > 1:
396
+ log.warning(
397
+ "Have multiple active classes and will convert to "
398
+ "lowest class")
399
+ else:
400
+ if np.max(np.sum(this_y, axis=1)) > 1:
401
+ log.warning(
402
+ "Have multiple active classes and will convert to "
403
+ "lowest class")
404
+ this_new_y = np.argmax(this_y, axis=1)
405
+ this_new_y[np.sum(this_y, axis=1) == 0] = -1
406
+ new_y.append(this_new_y)
407
+ y = new_y
408
+ if one_label_per_trial:
409
+ y = np.array(y, dtype=np.int64)
410
+
411
+ return SignalAndTarget(X, y)
412
+
413
+
414
+ def create_signal_target_with_breaks_from_mne(
415
+ cnt, name_to_start_codes,
416
+ trial_epoch_ival_ms,
417
+ name_to_stop_codes,
418
+ min_break_length_ms, max_break_length_ms,
419
+ break_epoch_ival_ms,
420
+ prepad_trials_to_n_samples=None):
421
+ """
422
+ Create SignalTarget set from given `mne.io.RawArray`.
423
+
424
+ Parameters
425
+ ----------
426
+ cnt: `mne.io.RawArray`
427
+ name_to_start_codes: OrderedDict (str -> int or list of int)
428
+ Ordered dictionary mapping class names to marker code or marker codes.
429
+ y-labels will be assigned in increasing key order, i.e.
430
+ first classname gets y-value 0, second classname y-value 1, etc.
431
+ trial_epoch_ival_ms: iterable of (int,int)
432
+ Epoching interval in milliseconds. Represents offset from start marker
433
+ and offset from stop marker. E.g. [500, -500] would mean 500ms
434
+ after the start marker until 500 ms before the stop marker.
435
+ name_to_stop_codes: dict (str -> int or list of int), optional
436
+ Dictionary mapping class names to stop marker code or stop marker codes.
437
+ Order does not matter, dictionary should contain each class in
438
+ `name_to_codes` dictionary.
439
+ min_break_length_ms: number
440
+ Breaks below this length are excluded.
441
+ max_break_length_ms: number
442
+ Breaks above this length are excluded.
443
+ break_epoch_ival_ms: number
444
+ Break ival, offset from trial end to start of the break in ms and
445
+ offset from trial start to end of break in ms.
446
+ prepad_trials_to_n_samples: int
447
+ Pad trials that would be too short with the signal before it (only
448
+ valid if name_to_stop_codes is not None).
449
+
450
+ Returns
451
+ -------
452
+ dataset: :class:`.SignalAndTarget`
453
+ Dataset with `X` as the trial signals and `y` as the trial labels.
454
+ Labels as timeseries and of integers, i.e., not one-hot encoded.
455
+ """
456
+ assert 'Break' not in name_to_start_codes
457
+ # Create new marker codes for start and stop of breaks
458
+ # Use marker codes that did not exist in the given marker codes...
459
+ all_start_codes = np.concatenate(
460
+ [np.atleast_1d(vals) for vals in name_to_start_codes.values()])
461
+ all_stop_codes = np.concatenate(
462
+ [np.atleast_1d(vals) for vals in name_to_stop_codes.values()])
463
+ break_start_code = -1
464
+ while break_start_code in np.concatenate((all_start_codes, all_stop_codes)):
465
+ break_start_code -= 1
466
+ break_stop_code = break_start_code - 1
467
+ while break_stop_code in np.concatenate((all_start_codes, all_stop_codes)):
468
+ break_stop_code -= 1
469
+
470
+ events = cnt.info['events'][:, [0, 2]]
471
+ # later trial segment ival will be added when creating set
472
+ # so remove it here
473
+ break_epoch_ival_ms = np.array(break_epoch_ival_ms) - (
474
+ np.array(trial_epoch_ival_ms))
475
+ events_with_breaks = add_breaks(events, cnt.info['sfreq'],
476
+ break_start_code, break_stop_code,
477
+ name_to_start_codes, name_to_stop_codes,
478
+ min_break_length_ms=min_break_length_ms,
479
+ max_break_length_ms=max_break_length_ms,
480
+ break_start_offset_ms=break_epoch_ival_ms[
481
+ 0],
482
+ break_stop_offset_ms=break_epoch_ival_ms[
483
+ 1])
484
+
485
+ name_to_start_codes_with_breaks = deepcopy(name_to_start_codes)
486
+ name_to_start_codes_with_breaks['Break'] = break_start_code
487
+ name_to_stop_codes_with_breaks = deepcopy(name_to_stop_codes)
488
+ name_to_stop_codes_with_breaks['Break'] = break_stop_code
489
+
490
+
491
+ data = cnt.get_data()
492
+ fs = cnt.info['sfreq']
493
+ signal_target = create_signal_target(
494
+ data, events_with_breaks, fs,
495
+ name_to_start_codes_with_breaks, trial_epoch_ival_ms,
496
+ name_to_stop_codes_with_breaks,
497
+ prepad_trials_to_n_samples=prepad_trials_to_n_samples,
498
+ one_hot_labels=False,
499
+ one_label_per_trial=False)
500
+
501
+ return signal_target
502
+
503
+
504
+ def add_breaks(
505
+ events, fs, break_start_code, break_stop_code, name_to_start_codes,
506
+ name_to_stop_codes, min_break_length_ms=None,
507
+ max_break_length_ms=None, break_start_offset_ms=None,
508
+ break_stop_offset_ms=None):
509
+ """
510
+ Add break events to given events.
511
+
512
+ Parameters
513
+ ----------
514
+ events: 2d-array
515
+ Dimensions: Number of events, 2. For each event, should contain sample
516
+ index and marker code.
517
+ fs: number
518
+ Sampling rate.
519
+ break_start_code: int
520
+ Marker code that will be used for break start markers.
521
+ break_stop_code: int
522
+ Marker code that will be used for break stop markers.
523
+ name_to_start_codes: OrderedDict (str -> int or list of int)
524
+ Ordered dictionary mapping class names to start marker code or
525
+ start marker codes.
526
+ name_to_stop_codes: dict (str -> int or list of int), optional
527
+ Dictionary mapping class names to stop marker code or stop marker codes.
528
+ min_break_length_ms: number, optional
529
+ Minimum length in milliseconds a break should have to be included.
530
+ max_break_length_ms: number, optional
531
+ Maximum length in milliseconds a break can have to be included.
532
+ break_start_offset_ms: number, optional
533
+ What offset from trial end to start of the break in ms.
534
+ break_stop_offset_ms: number, optional
535
+ What offset from next trial start end to previous break end in ms.
536
+
537
+ Returns
538
+ -------
539
+ events: 2d-array
540
+ Events with break start and stop markers.
541
+ """
542
+ min_samples = (None if min_break_length_ms is None
543
+ else ms_to_samples(min_break_length_ms, fs))
544
+ max_samples = (None if max_break_length_ms is None
545
+ else ms_to_samples(max_break_length_ms, fs))
546
+ orig_events = events
547
+ break_starts, break_stops = _extract_break_start_stop_ms(
548
+ events, name_to_start_codes, name_to_stop_codes)
549
+
550
+ break_durations = break_stops - break_starts
551
+ valid_mask = np.array([True] * len(break_starts))
552
+ if min_samples is not None:
553
+ valid_mask[break_durations < min_samples] = False
554
+ if max_samples is not None:
555
+ valid_mask[break_durations > max_samples] = False
556
+ if sum(valid_mask) == 0:
557
+ return deepcopy(events)
558
+ break_starts = break_starts[valid_mask]
559
+ break_stops = break_stops[valid_mask]
560
+ if break_start_offset_ms is not None:
561
+ break_starts += int(round(ms_to_samples(break_start_offset_ms, fs)))
562
+ if break_stop_offset_ms is not None:
563
+ break_stops += int(round(ms_to_samples(break_stop_offset_ms, fs)))
564
+ break_events = np.zeros((len(break_starts) * 2, 2))
565
+ break_events[0::2, 0] = break_starts
566
+ break_events[1::2, 0] = break_stops
567
+ break_events[0::2, 1] = break_start_code
568
+ break_events[1::2, 1] = break_stop_code
569
+
570
+ new_events = np.concatenate((orig_events, break_events))
571
+ # sort events
572
+ sort_order = np.argsort(new_events[:, 0], kind='mergesort')
573
+ new_events = new_events[sort_order]
574
+ return new_events
575
+
576
+
577
+ def _extract_break_start_stop_ms(events, name_to_start_codes,
578
+ name_to_stop_codes):
579
+ assert len(events[0]) == 2, "expect only 2dimensional event array here"
580
+ start_code_to_name_and_y = _to_mrk_code_to_name_and_y(name_to_start_codes)
581
+ # Ensure all stop marker codes are iterables
582
+ for name in name_to_stop_codes:
583
+ codes = name_to_stop_codes[name]
584
+ if not hasattr(codes, '__len__'):
585
+ name_to_stop_codes[name] = [codes]
586
+ all_stop_codes = np.concatenate(list(name_to_stop_codes.values())).astype(
587
+ np.int32)
588
+ event_samples = events[:, 0]
589
+ event_codes = events[:, 1]
590
+
591
+ break_starts = []
592
+ break_stops = []
593
+ i_event = 0
594
+ while i_event < len(events):
595
+ while (i_event < len(events)) and (
596
+ event_codes[i_event] not in all_stop_codes):
597
+ i_event += 1
598
+ if i_event < len(events):
599
+ # one sample after start
600
+ stop_sample = event_samples[i_event]
601
+ stop_code = event_codes[i_event]
602
+ i_event += 1
603
+ while (i_event < len(events)) and (
604
+ event_codes[i_event] not in start_code_to_name_and_y):
605
+ if event_codes[i_event] in all_stop_codes:
606
+ log.warning(
607
+ "New end marker {:.0f} at {:.0f} samples found, "
608
+ "no start marker for earlier end marker {:.0f} "
609
+ "at {:.0f} samples found.".format(
610
+ event_codes[i_event],
611
+ event_samples[i_event],
612
+ stop_code, stop_sample))
613
+ stop_sample = event_samples[i_event]
614
+ stop_code = event_codes[i_event]
615
+ i_event += 1
616
+
617
+ if i_event == len(events):
618
+ break
619
+
620
+ start_sample = event_samples[i_event]
621
+ start_code = event_codes[i_event]
622
+ assert start_code in start_code_to_name_and_y
623
+ # let's start one after stop of the trial and stop one efore
624
+ # start of the trial to ensure that markers will be
625
+ # in right order
626
+ break_starts.append(stop_sample + 1)
627
+ break_stops.append(start_sample - 1)
628
+ return np.array(break_starts), np.array(break_stops)