From 3392aae8fdba203d48a08886daa86d0f07a27465 Mon Sep 17 00:00:00 2001 From: Jan Sosulski Date: Thu, 6 Feb 2020 16:52:00 +0100 Subject: [PATCH 01/31] allow event lists in P300 paradigm --- moabb/paradigms/p300.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/moabb/paradigms/p300.py b/moabb/paradigms/p300.py index 4e21c91e3..a54caee33 100644 --- a/moabb/paradigms/p300.py +++ b/moabb/paradigms/p300.py @@ -99,6 +99,14 @@ def process_raw(self, raw, dataset): # pick events, based on event_id try: + if type(event_id['Target']) == list: + if type(event_id['NonTarget']) == list: + event_id_new = dict(Target=1, NonTarget=0) + events = mne.merge_events(events, event_id['Target'], 1) + events = mne.merge_events(events, event_id['NonTarget'], 0) + event_id = event_id_new + else: + raise ValueError('Please provide both Target/NonTarget event ids in lists or neither.') events = mne.pick_events(events, include=list(event_id.values())) except RuntimeError: # skip raw if no event found From eb20bc9710f89775c5eca5de2418af13a748c8b9 Mon Sep 17 00:00:00 2001 From: Jan Sosulski Date: Tue, 30 Jun 2020 18:07:57 +0200 Subject: [PATCH 02/31] merge two if-lines + compare type equality with is instead of == --- moabb/paradigms/p300.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/moabb/paradigms/p300.py b/moabb/paradigms/p300.py index a05c84c57..2d4d6d00d 100644 --- a/moabb/paradigms/p300.py +++ b/moabb/paradigms/p300.py @@ -99,14 +99,11 @@ def process_raw(self, raw, dataset, return_epochs=False): # pick events, based on event_id try: - if type(event_id['Target']) == list: - if type(event_id['NonTarget']) == list: - event_id_new = dict(Target=1, NonTarget=0) - events = mne.merge_events(events, event_id['Target'], 1) - events = mne.merge_events(events, event_id['NonTarget'], 0) - event_id = event_id_new - else: - raise ValueError('Please provide both Target/NonTarget event ids in lists or neither.') + if type(event_id['Target']) is list and type(event_id['NonTarget']) == list: + event_id_new = dict(Target=1, NonTarget=0) + events = mne.merge_events(events, event_id['Target'], 1) + events = mne.merge_events(events, event_id['NonTarget'], 0) + event_id = event_id_new events = mne.pick_events(events, include=list(event_id.values())) except RuntimeError: # skip raw if no event found From 78290807bea5fa00358c57bdee67e3edf3c7f8e7 Mon Sep 17 00:00:00 2001 From: Jan Sosulski Date: Tue, 30 Jun 2020 18:37:10 +0200 Subject: [PATCH 03/31] fix EPFL dataset by removing flat signal sections --- moabb/datasets/epfl.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/moabb/datasets/epfl.py b/moabb/datasets/epfl.py index 0b12900d1..9b90dd560 100644 --- a/moabb/datasets/epfl.py +++ b/moabb/datasets/epfl.py @@ -116,6 +116,13 @@ def _get_single_run_data(self, file_path): 'MA2'] ch_types = ['eeg'] * 32 + ['misc'] * 2 + # The last X entries are 0 for all signals. This leads to + # artifacts when epoching and band-pass filtering the data. + # Correct the signals for this. + sig_i = np.where( + np.diff(np.all(signals == 0, axis=0).astype(int)) != 0)[0][0] + signals = signals[:, :sig_i] + signals *= 1e-6 # data is stored as uV, but MNE expects V # we have to re-reference the signals # the average signal on the mastoids electrodes is used as reference references = [32, 33] From 216d529e5a71d16d3e6e97e0d4f607d869f5bdd0 Mon Sep 17 00:00:00 2001 From: robintibor Date: Fri, 3 Jul 2020 11:10:39 +0200 Subject: [PATCH 04/31] Fix for Python3.8 otherwise error TypeError: only integer scalar arrays can be converted to a scalar index (c is single-element-1d-array apparently) --- moabb/datasets/schirrmeister2017.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moabb/datasets/schirrmeister2017.py b/moabb/datasets/schirrmeister2017.py index 451ca3f56..7606a8aa3 100644 --- a/moabb/datasets/schirrmeister2017.py +++ b/moabb/datasets/schirrmeister2017.py @@ -198,7 +198,7 @@ def get_all_sensors(filename, pattern=None): """ with h5py.File(filename, 'r') as h5file: clab_set = h5file['nfo']['clab'][:].squeeze() - all_sensor_names = [''.join(chr(c) for c in h5file[obj_ref]) for + all_sensor_names = [''.join(chr(c.squeeze()) for c in h5file[obj_ref]) for obj_ref in clab_set] if pattern is not None: all_sensor_names = filter( From 63297a9a7c07d595ff3502923de4cc6f6c8ef3df Mon Sep 17 00:00:00 2001 From: robintibor Date: Fri, 3 Jul 2020 11:18:36 +0200 Subject: [PATCH 05/31] fix line length --- moabb/datasets/schirrmeister2017.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/moabb/datasets/schirrmeister2017.py b/moabb/datasets/schirrmeister2017.py index 7606a8aa3..40207e542 100644 --- a/moabb/datasets/schirrmeister2017.py +++ b/moabb/datasets/schirrmeister2017.py @@ -198,8 +198,9 @@ def get_all_sensors(filename, pattern=None): """ with h5py.File(filename, 'r') as h5file: clab_set = h5file['nfo']['clab'][:].squeeze() - all_sensor_names = [''.join(chr(c.squeeze()) for c in h5file[obj_ref]) for - obj_ref in clab_set] + all_sensor_names = [''.join( + chr(c.squeeze()) for c in h5file[obj_ref]) + for obj_ref in clab_set] if pattern is not None: all_sensor_names = filter( lambda sname: re.search(pattern, sname), From ed4c98dea588c8b879c01ed229c80f531aebcf97 Mon Sep 17 00:00:00 2001 From: Sylvain Chevallier Date: Mon, 13 Jul 2020 14:26:56 +0200 Subject: [PATCH 06/31] correct event detection and duplicate event --- moabb/paradigms/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index fd5804b6a..a92d5bd12 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -105,13 +105,12 @@ def process_raw(self, raw, dataset, return_epochs=False): event_id = self.used_events(dataset) # find the events, first check stim_channels then annotations - stim_channels = mne.utils._get_stim_channel( - None, raw.info, raise_error=False) + stim_channels = mne.utils._get_stim_channel(None, raw.info, + raise_error=False) if len(stim_channels) > 0: events = mne.find_events(raw, shortest_event=0, verbose=False) else: - events, _ = mne.events_from_annotations(raw, event_id=event_id, - verbose=False) + events, _ = mne.events_from_annotations(raw, verbose=False) channels = () if self.channels is None else self.channels # picks channels @@ -143,6 +142,7 @@ def process_raw(self, raw, dataset, return_epochs=False): tmin=tmin, tmax=tmax, proj=False, baseline=None, preload=True, verbose=False, picks=picks, + event_repeated='drop', on_missing='ignore') if self.resample is not None: epochs = epochs.resample(self.resample) From 131aa0fcaac3b2db6e7dbcdd40020e9e5f9000e5 Mon Sep 17 00:00:00 2001 From: Sylvain Chevallier Date: Mon, 13 Jul 2020 23:55:42 +0200 Subject: [PATCH 07/31] correct channel selection error and add a tutorial --- moabb/paradigms/base.py | 7 +- tutorials/select_electrodes_resample.py | 88 +++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 tutorials/select_electrodes_resample.py diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index a92d5bd12..5a8f0eb47 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -111,11 +111,12 @@ def process_raw(self, raw, dataset, return_epochs=False): events = mne.find_events(raw, shortest_event=0, verbose=False) else: events, _ = mne.events_from_annotations(raw, verbose=False) - channels = () if self.channels is None else self.channels # picks channels - picks = mne.pick_types(raw.info, eeg=True, stim=False, - include=channels) + if self.channels is None: + picks = mne.pick_types(raw.info, eeg=True, stim=False) + else: + picks = mne.pick_types(raw.info, stim=False, include=self.channels) # pick events, based on event_id try: diff --git a/tutorials/select_electrodes_resample.py b/tutorials/select_electrodes_resample.py new file mode 100644 index 000000000..3ec8fa4bb --- /dev/null +++ b/tutorials/select_electrodes_resample.py @@ -0,0 +1,88 @@ +""" +================================ +Select electrodes and resampling +================================ + +Within paradigm, it is possible to restrict analysis only to a subset of +electrodes and to resample to a specific sampling rate. There is also a +utility function to select common electrodes shared between datasets. +This tutorial demonstrates how to use this functionality. +""" +# Authors: Sylvain Chevallier +# +# License: BSD (3-clause) +from moabb.datasets import BNCI2014001, Zhou2016 +from moabb.paradigms import LeftRightImagery +from moabb.evaluations import WithinSessionEvaluation +from moabb.datasets.utils import find_intersecting_channels + +from sklearn.pipeline import make_pipeline +from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA +from sklearn.linear_model import LogisticRegression as LR + +from mne.decoding import CSP +from pyriemann.estimation import Covariances +from pyriemann.tangentspace import TangentSpace + +import matplotlib.pyplot as plt +import moabb.analysis.plotting as moabb_plt + +############################################################################## +# Datasets +# -------- +# +# Select datasets for motor imagery + +datasets = [Zhou2016(), BNCI2014001()] + +############################################################################## +# Paradigm +# -------- +# +# Restrict further analysis to specified channels, here C3, C4, and Cz. +# Also, use a specific resampling. In this example, all datasets are +# set to 200 Hz. + +paradigm = LeftRightImagery(channels=['C3', 'C4', 'Cz'], resample=200.) + +############################################################################## +# Evaluation +# ---------- +# +# The evaluation is conducted on with CSP+LDA, only on the 3 electrodes, with +# a sampling rate of 200 Hz. + +evaluation = WithinSessionEvaluation(paradigm=paradigm, + datasets=datasets) +csp_lda = make_pipeline(CSP(n_components=2), LDA()) +ts_lr = make_pipeline(Covariances(estimator='oas'), + TangentSpace(metric='riemann'), + LR(C=1.0)) +results = evaluation.process({'csp+lda': csp_lda, 'ts+lr': ts_lr}) +print(results.head()) + +############################################################################## +# Electrode selection +# ------------------- +# +# It is possible to select the electrodes that are shared by all datasets +# using the `find_intersecting_channels` function. Datasets that have 0 +# overlap with others are discarded. It returns the set of common channels, +# as well as the list of datasets with valid channels. + +electrodes, datasets = find_intersecting_channels(datasets) +evaluation = WithinSessionEvaluation(paradigm=paradigm, + datasets=datasets, + overwrite=True) +results = evaluation.process({'csp+lda': csp_lda, 'ts+lr': ts_lr}) +print(results.head()) + +############################################################################## +# Plot results +# ------------ +# +# Compare the obtained results with the two pipelines, CSP+LDA and logistic +# regression computed in the tangent space of the covariance matrices. + +fig = moabb_plt.paired_plot(results, 'csp+lda', 'ts+lr') +plt.show() From 980628847b26c41f24bcdc05b43a23430d246d3d Mon Sep 17 00:00:00 2001 From: Sylvain Chevallier Date: Fri, 17 Jul 2020 17:59:53 +0200 Subject: [PATCH 08/31] use gdf reader instead of edf --- moabb/datasets/upper_limb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moabb/datasets/upper_limb.py b/moabb/datasets/upper_limb.py index 40b458bc1..f8d2b83f2 100644 --- a/moabb/datasets/upper_limb.py +++ b/moabb/datasets/upper_limb.py @@ -1,6 +1,6 @@ from moabb.datasets.base import BaseDataset -from mne.io import read_raw_edf +from mne.io import read_raw_gdf from mne.channels import make_standard_montage import numpy as np @@ -94,7 +94,7 @@ def _get_single_subject_data(self, subject): montage = make_standard_montage('standard_1005') data = {} for ii, path in enumerate(paths): - raw = read_raw_edf(path, eog=eog, misc=range(64, 96), + raw = read_raw_gdf(path, eog=eog, misc=range(64, 96), preload=True, verbose='ERROR') raw.set_montage(montage) # there is nan in the data From 480ed32a3677d809edb2192742cdb2168101dbef Mon Sep 17 00:00:00 2001 From: Sylvain Chevallier Date: Fri, 17 Jul 2020 18:02:48 +0200 Subject: [PATCH 09/31] read events from annotation --- moabb/paradigms/base.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index a92d5bd12..00a0f5f3c 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -110,7 +110,13 @@ def process_raw(self, raw, dataset, return_epochs=False): if len(stim_channels) > 0: events = mne.find_events(raw, shortest_event=0, verbose=False) else: - events, _ = mne.events_from_annotations(raw, verbose=False) + ev_selected = {str(v):v for v in event_id.values()} + try: + events, _ = mne.events_from_annotations(raw, + event_id=ev_selected, + verbose=False) + except ValueError: + events, _ = mne.events_from_annotations(raw, verbose=False) channels = () if self.channels is None else self.channels # picks channels From 9463a6f8e9bbb3f0726e6e95d69f86d7fe4bbbe1 Mon Sep 17 00:00:00 2001 From: Sylvain Chevallier Date: Fri, 17 Jul 2020 18:16:30 +0200 Subject: [PATCH 10/31] correct merge --- moabb/paradigms/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index c6d0d624c..d4943a34a 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -117,7 +117,6 @@ def process_raw(self, raw, dataset, return_epochs=False): verbose=False) except ValueError: events, _ = mne.events_from_annotations(raw, verbose=False) - channels = () if self.channels is None else self.channels # picks channels if self.channels is None: From 53bb58e4c7368601ccc1a955a2dcbe4a698ba222 Mon Sep 17 00:00:00 2001 From: Sylvain Chevallier Date: Fri, 17 Jul 2020 18:20:36 +0200 Subject: [PATCH 11/31] correct style --- moabb/paradigms/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index d4943a34a..b4bc79446 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -110,7 +110,7 @@ def process_raw(self, raw, dataset, return_epochs=False): if len(stim_channels) > 0: events = mne.find_events(raw, shortest_event=0, verbose=False) else: - ev_selected = {str(v):v for v in event_id.values()} + ev_selected = {str(v): v for v in event_id.values()} try: events, _ = mne.events_from_annotations(raw, event_id=ev_selected, From 29f009bd6f876b4e63d1b96024d042b25c5783ae Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Fri, 31 Jul 2020 23:01:27 -0300 Subject: [PATCH 12/31] Add event_id --- moabb/paradigms/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index 5a8f0eb47..a2da8c4d5 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -110,7 +110,8 @@ def process_raw(self, raw, dataset, return_epochs=False): if len(stim_channels) > 0: events = mne.find_events(raw, shortest_event=0, verbose=False) else: - events, _ = mne.events_from_annotations(raw, verbose=False) + events, _ = mne.events_from_annotations(raw, event_id=event_id, + verbose=False) # picks channels if self.channels is None: From b354edc6f60dd3bac4c59bd58e548ae46d60bedd Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Wed, 19 Aug 2020 21:21:35 -0300 Subject: [PATCH 13/31] Add baseline baseline correction --- moabb/paradigms/base.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index b4bc79446..572aedb68 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -62,7 +62,7 @@ def prepare_process(self, dataset): """ pass - def process_raw(self, raw, dataset, return_epochs=False): + def process_raw(self, raw, dataset, baseline=None, return_epochs=False): """ Process one raw data file. @@ -75,14 +75,16 @@ def process_raw(self, raw, dataset, return_epochs=False): Parameters ---------- - raw: mne.Raw instance the raw EEG data. - dataset : dataset instance The dataset corresponding to the raw file. mainly use to access - dataset specific information. - + dataset specific information. + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline correction. + If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), including the endpoints. + Correction is applied by computing the mean of the baseline period and subtracting it from the data (see mne.Epochs) return_epochs: boolean This flag specifies whether to return only the data array or the complete processed mne.Epochs @@ -93,10 +95,8 @@ def process_raw(self, raw, dataset, return_epochs=False): the data that will be used as features for the model Note: if return_epochs=True, this is mne.Epochs if return_epochs=False, this is np.ndarray - labels: np.ndarray the labels for training / evaluating the model - metadata: pd.DataFrame A dataframe containing the metadata @@ -110,10 +110,9 @@ def process_raw(self, raw, dataset, return_epochs=False): if len(stim_channels) > 0: events = mne.find_events(raw, shortest_event=0, verbose=False) else: - ev_selected = {str(v): v for v in event_id.values()} try: events, _ = mne.events_from_annotations(raw, - event_id=ev_selected, + event_id=event_id, verbose=False) except ValueError: events, _ = mne.events_from_annotations(raw, verbose=False) @@ -145,12 +144,13 @@ def process_raw(self, raw, dataset, return_epochs=False): raw_f = raw.copy().filter(fmin, fmax, method='iir', picks=picks, verbose=False) # epoch data - epochs = mne.Epochs(raw_f, events, event_id=event_id, - tmin=tmin, tmax=tmax, proj=False, - baseline=None, preload=True, + epochs = mne.Epochs(raw_f, events, event_id=event_id, proj=False, + tmin=baseline[0], tmax=dataset.interval[1], + baseline=baseline, preload=True, verbose=False, picks=picks, event_repeated='drop', on_missing='ignore') + epochs.crop(tmin=tmin, tmax=tmax) if self.resample is not None: epochs = epochs.resample(self.resample) # rescale to work with uV @@ -171,7 +171,8 @@ def process_raw(self, raw, dataset, return_epochs=False): metadata = pd.DataFrame(index=range(len(labels))) return X, labels, metadata - def get_data(self, dataset, subjects=None, return_epochs=False): + def get_data(self, dataset, subjects=None, baseline=None, + return_epochs=False): """ Return the data for a list of subject. @@ -188,11 +189,21 @@ def get_data(self, dataset, subjects=None, return_epochs=False): A dataset instance. subjects: List of int List of subject number + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline correction. + If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), including the endpoints. + Correction is applied by computing the mean of the baseline period and subtracting it from the data (see mne.Epochs) + return_epochs: boolean + This flag specifies whether to return only the data array or the + complete processed mne.Epochs returns ------- - X : np.ndarray + X : Union[np.ndarray, mne.Epochs] the data that will be used as features for the model + Note: if return_epochs=True, this is mne.Epochs + if return_epochs=False, this is np.ndarray labels: np.ndarray the labels for training / evaluating the model metadata: pd.DataFrame @@ -213,7 +224,7 @@ def get_data(self, dataset, subjects=None, return_epochs=False): for subject, sessions in data.items(): for session, runs in sessions.items(): for run, raw in runs.items(): - proc = self.process_raw(raw, dataset, + proc = self.process_raw(raw, dataset, baseline=baseline, return_epochs=return_epochs) if proc is None: From 7afed9d0e16cbfc387b1a92e5f9788aa1d496b24 Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Wed, 19 Aug 2020 23:35:20 -0300 Subject: [PATCH 14/31] Fix 'NoneType' error --- moabb/paradigms/base.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index 572aedb68..fd3ef3591 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -144,13 +144,20 @@ def process_raw(self, raw, dataset, baseline=None, return_epochs=False): raw_f = raw.copy().filter(fmin, fmax, method='iir', picks=picks, verbose=False) # epoch data + if baseline is not None: + bmin = baseline[0] if baseline[0] < tmin else tmin + bmax = baseline[1] if baseline[1] > tmax else tmax + else: + bmin = tmin + bmax = tmax epochs = mne.Epochs(raw_f, events, event_id=event_id, proj=False, tmin=baseline[0], tmax=dataset.interval[1], baseline=baseline, preload=True, verbose=False, picks=picks, event_repeated='drop', on_missing='ignore') - epochs.crop(tmin=tmin, tmax=tmax) + if bmin < tmin or bmax > tmax: + epochs.crop(tmin=tmin, tmax=tmax) if self.resample is not None: epochs = epochs.resample(self.resample) # rescale to work with uV From 55e6c63b318dc6d2926c7183dc4299dc10721678 Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Wed, 19 Aug 2020 23:39:20 -0300 Subject: [PATCH 15/31] Update base.py --- moabb/paradigms/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index fd3ef3591..5724f3132 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -151,7 +151,7 @@ def process_raw(self, raw, dataset, baseline=None, return_epochs=False): bmin = tmin bmax = tmax epochs = mne.Epochs(raw_f, events, event_id=event_id, proj=False, - tmin=baseline[0], tmax=dataset.interval[1], + tmin=bmin, tmax=bmax, baseline=baseline, preload=True, verbose=False, picks=picks, event_repeated='drop', From 3a633e78af78c198838456c18a11afe335ca9a05 Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Thu, 20 Aug 2020 00:20:32 -0300 Subject: [PATCH 16/31] Fix PEP8 errors --- moabb/paradigms/base.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index 5724f3132..a390356cb 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -79,12 +79,14 @@ def process_raw(self, raw, dataset, baseline=None, return_epochs=False): the raw EEG data. dataset : dataset instance The dataset corresponding to the raw file. mainly use to access - dataset specific information. + dataset specific information. baseline: None | tuple of length 2 - The time interval to consider as “baseline” when applying baseline correction. - If None, do not apply baseline correction. - If a tuple (a, b), the interval is between a and b (in seconds), including the endpoints. - Correction is applied by computing the mean of the baseline period and subtracting it from the data (see mne.Epochs) + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) return_epochs: boolean This flag specifies whether to return only the data array or the complete processed mne.Epochs @@ -197,10 +199,12 @@ def get_data(self, dataset, subjects=None, baseline=None, subjects: List of int List of subject number baseline: None | tuple of length 2 - The time interval to consider as “baseline” when applying baseline correction. - If None, do not apply baseline correction. - If a tuple (a, b), the interval is between a and b (in seconds), including the endpoints. - Correction is applied by computing the mean of the baseline period and subtracting it from the data (see mne.Epochs) + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) return_epochs: boolean This flag specifies whether to return only the data array or the complete processed mne.Epochs From f1b6ac9a37f270dfc92f2e4e01fdb434237d8823 Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Thu, 20 Aug 2020 12:04:41 -0300 Subject: [PATCH 17/31] moved baseline property to MotorImagery --- moabb/paradigms/base.py | 24 ++++++------------------ moabb/paradigms/motor_imagery.py | 13 +++++++++++-- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index a390356cb..e001c1f93 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -62,7 +62,7 @@ def prepare_process(self, dataset): """ pass - def process_raw(self, raw, dataset, baseline=None, return_epochs=False): + def process_raw(self, raw, dataset, return_epochs=False): """ Process one raw data file. @@ -80,13 +80,6 @@ def process_raw(self, raw, dataset, baseline=None, return_epochs=False): dataset : dataset instance The dataset corresponding to the raw file. mainly use to access dataset specific information. - baseline: None | tuple of length 2 - The time interval to consider as “baseline” when applying baseline - correction. If None, do not apply baseline correction. - If a tuple (a, b), the interval is between a and b (in seconds), - including the endpoints. - Correction is applied by computing the mean of the baseline period - and subtracting it from the data (see mne.Epochs) return_epochs: boolean This flag specifies whether to return only the data array or the complete processed mne.Epochs @@ -146,7 +139,10 @@ def process_raw(self, raw, dataset, baseline=None, return_epochs=False): raw_f = raw.copy().filter(fmin, fmax, method='iir', picks=picks, verbose=False) # epoch data + baseline = self.baseline if baseline is not None: + baseline = (self.baseline[0] + dataset.interval[0], + self.baseline[1] + dataset.interval[0]) bmin = baseline[0] if baseline[0] < tmin else tmin bmax = baseline[1] if baseline[1] > tmax else tmax else: @@ -180,8 +176,7 @@ def process_raw(self, raw, dataset, baseline=None, return_epochs=False): metadata = pd.DataFrame(index=range(len(labels))) return X, labels, metadata - def get_data(self, dataset, subjects=None, baseline=None, - return_epochs=False): + def get_data(self, dataset, subjects=None, return_epochs=False): """ Return the data for a list of subject. @@ -198,13 +193,6 @@ def get_data(self, dataset, subjects=None, baseline=None, A dataset instance. subjects: List of int List of subject number - baseline: None | tuple of length 2 - The time interval to consider as “baseline” when applying baseline - correction. If None, do not apply baseline correction. - If a tuple (a, b), the interval is between a and b (in seconds), - including the endpoints. - Correction is applied by computing the mean of the baseline period - and subtracting it from the data (see mne.Epochs) return_epochs: boolean This flag specifies whether to return only the data array or the complete processed mne.Epochs @@ -235,7 +223,7 @@ def get_data(self, dataset, subjects=None, baseline=None, for subject, sessions in data.items(): for session, runs in sessions.items(): for run, raw in runs.items(): - proc = self.process_raw(raw, dataset, baseline=baseline, + proc = self.process_raw(raw, dataset, return_epochs=return_epochs) if proc is None: diff --git a/moabb/paradigms/motor_imagery.py b/moabb/paradigms/motor_imagery.py index 53802bb36..999f58874 100644 --- a/moabb/paradigms/motor_imagery.py +++ b/moabb/paradigms/motor_imagery.py @@ -36,6 +36,14 @@ class BaseMotorImagery(BaseParadigm): 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) + channels: list of str | None (default None) list of channel to select. If None, use all EEG channels available in the dataset. @@ -45,11 +53,12 @@ class BaseMotorImagery(BaseParadigm): """ def __init__(self, filters=([7, 35],), events=None, tmin=0.0, tmax=None, - channels=None, resample=None): + baseline=None, channels=None, resample=None): super().__init__() self.filters = filters - self.channels = channels self.events = events + self.channels = channels + self.baseline = baseline self.resample = resample if (tmax is not None): From af97c35945fae40c749e9cd7ec6b9e798674ef11 Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Thu, 20 Aug 2020 12:34:41 -0300 Subject: [PATCH 18/31] Baseline is added to all paradigms --- moabb/paradigms/base.py | 4 ++-- moabb/paradigms/motor_imagery.py | 16 ++++++++++++++++ moabb/paradigms/p300.py | 32 ++++++++++++++++++++++++++++++-- moabb/paradigms/ssvep.py | 27 ++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/moabb/paradigms/base.py b/moabb/paradigms/base.py index e001c1f93..b2c6c0e9b 100644 --- a/moabb/paradigms/base.py +++ b/moabb/paradigms/base.py @@ -148,8 +148,8 @@ def process_raw(self, raw, dataset, return_epochs=False): else: bmin = tmin bmax = tmax - epochs = mne.Epochs(raw_f, events, event_id=event_id, proj=False, - tmin=bmin, tmax=bmax, + epochs = mne.Epochs(raw_f, events, event_id=event_id, + tmin=bmin, tmax=bmax, proj=False, baseline=baseline, preload=True, verbose=False, picks=picks, event_repeated='drop', diff --git a/moabb/paradigms/motor_imagery.py b/moabb/paradigms/motor_imagery.py index 999f58874..9715a527c 100644 --- a/moabb/paradigms/motor_imagery.py +++ b/moabb/paradigms/motor_imagery.py @@ -129,6 +129,14 @@ class SinglePass(BaseMotorImagery): 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) + channels: list of str | None (default None) list of channel to select. If None, use all EEG channels available in the dataset. @@ -307,6 +315,14 @@ class MotorImagery(SinglePass): 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) + channels: list of str | None (default None) list of channel to select. If None, use all EEG channels available in the dataset. diff --git a/moabb/paradigms/p300.py b/moabb/paradigms/p300.py index e6bf29551..3b6a7c4f0 100644 --- a/moabb/paradigms/p300.py +++ b/moabb/paradigms/p300.py @@ -39,6 +39,14 @@ class BaseP300(BaseParadigm): 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) + channels: list of str | None (default None) list of channel to select. If None, use all EEG channels available in the dataset. @@ -48,11 +56,12 @@ class BaseP300(BaseParadigm): """ def __init__(self, filters=([1, 24],), events=None, tmin=0.0, tmax=None, - channels=None, resample=None): + baseline=None, channels=None, resample=None): super().__init__() self.filters = filters - self.channels = channels self.events = events + self.channels = channels + self.baseline = baseline self.resample = resample if (tmax is not None): @@ -118,11 +127,22 @@ def process_raw(self, raw, dataset, return_epochs=False): raw_f = raw.copy().filter(fmin, fmax, method='iir', picks=picks, verbose=False) # epoch data + baseline = self.baseline + if baseline is not None: + baseline = (self.baseline[0] + dataset.interval[0], + self.baseline[1] + dataset.interval[0]) + bmin = baseline[0] if baseline[0] < tmin else tmin + bmax = baseline[1] if baseline[1] > tmax else tmax + else: + bmin = tmin + bmax = tmax epochs = mne.Epochs(raw_f, events, event_id=event_id, tmin=tmin, tmax=tmax, proj=False, baseline=None, preload=True, verbose=False, picks=picks, on_missing='ignore') + if bmin < tmin or bmax > tmax: + epochs.crop(tmin=tmin, tmax=tmax) if self.resample is not None: epochs = epochs.resample(self.resample) # rescale to work with uV @@ -187,6 +207,14 @@ class SinglePass(BaseP300): 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) + channels: list of str | None (default None) list of channel to select. If None, use all EEG channels available in the dataset. diff --git a/moabb/paradigms/ssvep.py b/moabb/paradigms/ssvep.py index 9eddc96ae..4f0bff8dd 100644 --- a/moabb/paradigms/ssvep.py +++ b/moabb/paradigms/ssvep.py @@ -34,6 +34,14 @@ class BaseSSVEP(BaseParadigm): dataset specific task interval. tmax = 5 would mean the epoch will end 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) channels: list of str | None (default None) List of channel to select. If None, use all EEG channels available in @@ -44,11 +52,12 @@ class BaseSSVEP(BaseParadigm): """ def __init__(self, filters=[(7, 45)], events=None, n_classes=2, tmin=0.0, - tmax=None, channels=None, resample=None): + tmax=None, baseline= None, channels=None, resample=None): super().__init__() self.filters = filters self.events = events self.n_classes = n_classes + self.baseline = baseline self.channels = channels self.resample = resample @@ -154,6 +163,14 @@ class SSVEP(BaseSSVEP): dataset specific task interval. tmax = 5 would mean the epoch will end 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) channels: list of str | None (default None) List of channel to select. If None, use all EEG channels available in @@ -199,6 +216,14 @@ class FilterBankSSVEP(BaseSSVEP): dataset specific task interval. tmax = 5 would mean the epoch will end 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. + + baseline: None | tuple of length 2 + The time interval to consider as “baseline” when applying baseline + correction. If None, do not apply baseline correction. + If a tuple (a, b), the interval is between a and b (in seconds), + including the endpoints. + Correction is applied by computing the mean of the baseline period + and subtracting it from the data (see mne.Epochs) channels: list of str | None (default None) List of channel to select. If None, use all EEG channels available in From bfe64089b9402b422786d7ef2a07f688d4e25a8a Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Thu, 20 Aug 2020 12:37:58 -0300 Subject: [PATCH 19/31] Fix PEP8 errors --- moabb/paradigms/ssvep.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moabb/paradigms/ssvep.py b/moabb/paradigms/ssvep.py index 4f0bff8dd..109aad552 100644 --- a/moabb/paradigms/ssvep.py +++ b/moabb/paradigms/ssvep.py @@ -34,7 +34,7 @@ class BaseSSVEP(BaseParadigm): dataset specific task interval. tmax = 5 would mean the epoch will end 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. - + baseline: None | tuple of length 2 The time interval to consider as “baseline” when applying baseline correction. If None, do not apply baseline correction. @@ -163,7 +163,7 @@ class SSVEP(BaseSSVEP): dataset specific task interval. tmax = 5 would mean the epoch will end 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. - + baseline: None | tuple of length 2 The time interval to consider as “baseline” when applying baseline correction. If None, do not apply baseline correction. @@ -216,7 +216,7 @@ class FilterBankSSVEP(BaseSSVEP): dataset specific task interval. tmax = 5 would mean the epoch will end 5 second after the begining of the task as defined in the dataset. If None, use the dataset value. - + baseline: None | tuple of length 2 The time interval to consider as “baseline” when applying baseline correction. If None, do not apply baseline correction. From ba9e04375824cbbc9ae3e8b8176f2add4696e215 Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Thu, 20 Aug 2020 12:43:07 -0300 Subject: [PATCH 20/31] Fix PEP8 errors --- moabb/paradigms/ssvep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moabb/paradigms/ssvep.py b/moabb/paradigms/ssvep.py index 109aad552..80295fb64 100644 --- a/moabb/paradigms/ssvep.py +++ b/moabb/paradigms/ssvep.py @@ -52,7 +52,7 @@ class BaseSSVEP(BaseParadigm): """ def __init__(self, filters=[(7, 45)], events=None, n_classes=2, tmin=0.0, - tmax=None, baseline= None, channels=None, resample=None): + tmax=None, baseline=None, channels=None, resample=None): super().__init__() self.filters = filters self.events = events From bcea77a722dad3b99c0efb5fa862404ef9355f2d Mon Sep 17 00:00:00 2001 From: Ramiro Gatti Date: Fri, 21 Aug 2020 17:51:45 -0300 Subject: [PATCH 21/31] Fixed epochs parameters --- moabb/paradigms/p300.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moabb/paradigms/p300.py b/moabb/paradigms/p300.py index 3b6a7c4f0..652614842 100644 --- a/moabb/paradigms/p300.py +++ b/moabb/paradigms/p300.py @@ -137,8 +137,8 @@ def process_raw(self, raw, dataset, return_epochs=False): bmin = tmin bmax = tmax epochs = mne.Epochs(raw_f, events, event_id=event_id, - tmin=tmin, tmax=tmax, proj=False, - baseline=None, preload=True, + tmin=bmin, tmax=bmax, proj=False, + baseline=baseline, preload=True, verbose=False, picks=picks, on_missing='ignore') if bmin < tmin or bmax > tmax: From 2cc8c625e0cb918584c225952441eab8b12f1dba Mon Sep 17 00:00:00 2001 From: Jan Sosulski Date: Fri, 11 Dec 2020 14:28:07 +0100 Subject: [PATCH 22/31] fix P300 fake datasets in unit tests --- moabb/tests/paradigms.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/moabb/tests/paradigms.py b/moabb/tests/paradigms.py index 480b13fc0..21cf26c94 100644 --- a/moabb/tests/paradigms.py +++ b/moabb/tests/paradigms.py @@ -130,15 +130,15 @@ class Test_P300(unittest.TestCase): def test_BaseP300_paradigm(self): paradigm = SimpleP300() - dataset = FakeDataset(paradigm='p300') + dataset = FakeDataset(paradigm='p300', event_list=['Target', 'NonTarget']) X, labels, metadata = paradigm.get_data(dataset, subjects=[1]) # we should have all the same length self.assertEqual(len(X), len(labels), len(metadata)) # X must be a 3D Array self.assertEqual(len(X.shape), 3) - # labels must contain 3 values - self.assertEqual(len(np.unique(labels)), 3) + # labels must contain 2 values (Target/NonTarget) + self.assertEqual(len(np.unique(labels)), 2) # metadata must have subjets, sessions, runs self.assertTrue('subject' in metadata.columns) @@ -160,7 +160,7 @@ def test_BaseP300_tmintmax(self): def test_BaseP300_filters(self): # can work with filter bank paradigm = SimpleP300(filters=[[1, 12], [12, 24]]) - dataset = FakeDataset(paradigm='p300') + dataset = FakeDataset(paradigm='p300', event_list=['Target', 'NonTarget']) X, labels, metadata = paradigm.get_data(dataset, subjects=[1]) # X must be a 4D Array @@ -171,7 +171,7 @@ def test_BaseP300_wrongevent(self): # test process_raw return empty list if raw does not contain any # selected event. cetain runs in dataset are event specific. paradigm = SimpleP300(filters=[[1, 12], [12, 24]]) - dataset = FakeDataset(paradigm='p300') + dataset = FakeDataset(paradigm='p300', event_list=['Target', 'NonTarget']) raw = dataset.get_data([1])[1]['session_0']['run_0'] # add something on the event channel raw._data[-1] *= 10 From 1f4f7503f08b06bc9714042693ada5397a79d102 Mon Sep 17 00:00:00 2001 From: Jan Sosulski Date: Fri, 11 Dec 2020 16:56:46 +0100 Subject: [PATCH 23/31] fix flake8 errors --- moabb/paradigms/p300.py | 3 ++- moabb/tests/paradigms.py | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/moabb/paradigms/p300.py b/moabb/paradigms/p300.py index bfd1c02ab..e8b515fbe 100644 --- a/moabb/paradigms/p300.py +++ b/moabb/paradigms/p300.py @@ -108,7 +108,8 @@ def process_raw(self, raw, dataset, return_epochs=False): # pick events, based on event_id try: - if type(event_id['Target']) is list and type(event_id['NonTarget']) == list: + if (type(event_id['Target']) is list and + type(event_id['NonTarget']) == list): event_id_new = dict(Target=1, NonTarget=0) events = mne.merge_events(events, event_id['Target'], 1) events = mne.merge_events(events, event_id['NonTarget'], 0) diff --git a/moabb/tests/paradigms.py b/moabb/tests/paradigms.py index 21cf26c94..a3b177f8c 100644 --- a/moabb/tests/paradigms.py +++ b/moabb/tests/paradigms.py @@ -130,7 +130,8 @@ class Test_P300(unittest.TestCase): def test_BaseP300_paradigm(self): paradigm = SimpleP300() - dataset = FakeDataset(paradigm='p300', event_list=['Target', 'NonTarget']) + dataset = FakeDataset(paradigm='p300', + event_list=['Target', 'NonTarget']) X, labels, metadata = paradigm.get_data(dataset, subjects=[1]) # we should have all the same length @@ -160,7 +161,8 @@ def test_BaseP300_tmintmax(self): def test_BaseP300_filters(self): # can work with filter bank paradigm = SimpleP300(filters=[[1, 12], [12, 24]]) - dataset = FakeDataset(paradigm='p300', event_list=['Target', 'NonTarget']) + dataset = FakeDataset(paradigm='p300', + event_list=['Target', 'NonTarget']) X, labels, metadata = paradigm.get_data(dataset, subjects=[1]) # X must be a 4D Array @@ -171,7 +173,8 @@ def test_BaseP300_wrongevent(self): # test process_raw return empty list if raw does not contain any # selected event. cetain runs in dataset are event specific. paradigm = SimpleP300(filters=[[1, 12], [12, 24]]) - dataset = FakeDataset(paradigm='p300', event_list=['Target', 'NonTarget']) + dataset = FakeDataset(paradigm='p300', + event_list=['Target', 'NonTarget']) raw = dataset.get_data([1])[1]['session_0']['run_0'] # add something on the event channel raw._data[-1] *= 10 From ca773d50186e111babf25f3cfe0501d28d9fd54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Dec 2020 20:09:20 +0100 Subject: [PATCH 24/31] ci: added basic GitHub Actions config --- .github/workflows/test.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..f9117c416 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,35 @@ +name: Test + +on: + push: + branches: [ master, 'dev/*' ] + pull_request: + branches: [ master ] + +jobs: + test: + name: ${{ matrix.os }}, py-${{ matrix.python_version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04, windows-latest, macOS-latest] + python_version: [3.6] + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python_version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel flake8 + pip install -r requirements.txt + pip install . + - name: Run tests + run: | + python -m unittest moabb.tests + python -m moabb.run --pipelines=./moabb/tests/test_pipelines/ --verbose + - name: Run linting + run: | + flake8 moabb From ce1adf9d97fcd6bb7160d76b3efcf1ed4527ea4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Dec 2020 20:18:57 +0100 Subject: [PATCH 25/31] ci: use bash on all OSs --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9117c416..4d54f1bd2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,14 +22,17 @@ jobs: with: python-version: ${{ matrix.python_version }} - name: Install dependencies + shell: bash run: | python -m pip install --upgrade pip wheel flake8 pip install -r requirements.txt pip install . - name: Run tests + shell: bash run: | python -m unittest moabb.tests python -m moabb.run --pipelines=./moabb/tests/test_pipelines/ --verbose - name: Run linting + shell: bash run: | flake8 moabb From 13aae581a2fadc2b80085c8984555e33fe0b0dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Dec 2020 20:19:56 +0100 Subject: [PATCH 26/31] ci: remove ci on 'dev/*' push --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4d54f1bd2..0b6404b24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: Test on: push: - branches: [ master, 'dev/*' ] + branches: [ master ] pull_request: branches: [ master ] From 6dfe46cd0d43f334238ba7d585e0fbef77bd4c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Dec 2020 20:59:54 +0100 Subject: [PATCH 27/31] build(deps): version locked h5py due to https://github.com/NeuroTechX/moabb/issues/122 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 242ffeb23..51b217a8b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ mne >= 0.19 pyriemann matplotlib >= 2.2 seaborn >= 0.9.0 -h5py +h5py==2.10 # locked due to https://github.com/NeuroTechX/moabb/issues/122 pandas pyyaml coloredlogs From 83c110d2a745e9f58d65e6d434a9e17b54f2e1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Dec 2020 21:19:15 +0100 Subject: [PATCH 28/31] docs: added GitHub CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 341bbceee..5d410c889 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ **This is work in progress. API will change significantly (as well as the results of the benchmark).** -[![Build Status](https://travis-ci.org/NeuroTechX/moabb.svg?branch=master)](https://travis-ci.org/NeuroTechX/moabb) +[![Build Status](https://github.com/NeuroTechX/moabb/workflows/Test/badge.svg)](https://github.com/NeuroTechX/moabb/actions?query=branch%3Amaster) ## Welcome! From 91be7d9009954c0da60d0ac5e021e65f3d69af5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Dec 2020 21:29:45 +0100 Subject: [PATCH 29/31] fix: fixed LICENSE template (proper detection by licensee) --- LICENSE | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/LICENSE b/LICENSE index 1478d3165..6bf91f0a0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,24 +1,29 @@ -Copyright © 2017, authors of moabb +BSD 3-Clause License + +Copyright (c) 2017, authors of moabb All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the names of moabb authors nor the names of any - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 009d6fa52db5b9abce6096caf6744d9a0b26dc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Thu, 14 Jan 2021 13:11:38 +0100 Subject: [PATCH 30/31] build(deps): pinned scikit-learn to <0.24 (while waiting for pyriemann fix) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 51b217a8b..91754a47f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -scikit-learn +scikit-learn<0.24.0 # 0.24.0 breaks pyriemann, requires https://github.com/alexandrebarachant/pyRiemann/pull/93 mne >= 0.19 pyriemann matplotlib >= 2.2 From fdd9f464b8cd063725f58d8267380c3845af2ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 18 Dec 2020 20:09:20 +0100 Subject: [PATCH 31/31] ci: added basic GitHub Actions config ci: use bash on all OSs ci: remove ci on 'dev/*' push build(deps): version locked h5py due to https://github.com/NeuroTechX/moabb/issues/122 docs: added GitHub CI badge fix: fixed LICENSE template (proper detection by licensee) build(deps): pinned scikit-learn to <0.24 (while waiting for pyriemann fix) --- .github/workflows/test.yml | 38 +++++++++++++++++++++++++++++++++ LICENSE | 43 +++++++++++++++++++++----------------- README.md | 2 +- requirements.txt | 4 ++-- 4 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..0b6404b24 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test: + name: ${{ matrix.os }}, py-${{ matrix.python_version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04, windows-latest, macOS-latest] + python_version: [3.6] + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python_version }} + - name: Install dependencies + shell: bash + run: | + python -m pip install --upgrade pip wheel flake8 + pip install -r requirements.txt + pip install . + - name: Run tests + shell: bash + run: | + python -m unittest moabb.tests + python -m moabb.run --pipelines=./moabb/tests/test_pipelines/ --verbose + - name: Run linting + shell: bash + run: | + flake8 moabb diff --git a/LICENSE b/LICENSE index 1478d3165..6bf91f0a0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,24 +1,29 @@ -Copyright © 2017, authors of moabb +BSD 3-Clause License + +Copyright (c) 2017, authors of moabb All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the names of moabb authors nor the names of any - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 341bbceee..5d410c889 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ **This is work in progress. API will change significantly (as well as the results of the benchmark).** -[![Build Status](https://travis-ci.org/NeuroTechX/moabb.svg?branch=master)](https://travis-ci.org/NeuroTechX/moabb) +[![Build Status](https://github.com/NeuroTechX/moabb/workflows/Test/badge.svg)](https://github.com/NeuroTechX/moabb/actions?query=branch%3Amaster) ## Welcome! diff --git a/requirements.txt b/requirements.txt index 242ffeb23..91754a47f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ -scikit-learn +scikit-learn<0.24.0 # 0.24.0 breaks pyriemann, requires https://github.com/alexandrebarachant/pyRiemann/pull/93 mne >= 0.19 pyriemann matplotlib >= 2.2 seaborn >= 0.9.0 -h5py +h5py==2.10 # locked due to https://github.com/NeuroTechX/moabb/issues/122 pandas pyyaml coloredlogs