.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "generated/examples/40_decode.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_generated_examples_40_decode.py: Decoding real-time data ======================= .. include:: ./../../links.inc This example demonstrates how to decode real-time data using `MNE-Python `_ and `Scikit-learn `_. We will stream the ``sample_audvis_raw.fif`` file from MNE's sample dataset with a :class:`~mne_lsl.player.PlayerLSL`, process the signal through a :class:`~mne_lsl.stream.StreamLSL`, and decode the epochs created with :class:`~mne_lsl.stream.EpochsStream`. .. GENERATED FROM PYTHON SOURCE LINES 13-37 .. code-block:: Python import time import uuid import numpy as np from matplotlib import pyplot as plt from mne.decoding import Vectorizer from mne.io import read_raw_fif from sklearn.linear_model import LogisticRegression from sklearn.model_selection import ShuffleSplit, cross_val_score from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from mne_lsl.datasets import sample from mne_lsl.player import PlayerLSL from mne_lsl.stream import EpochsStream, StreamLSL fname = sample.data_path() / "mne-sample" / "sample_audvis_raw.fif" raw = read_raw_fif(fname, preload=False).pick(("meg", "stim")).load_data() source_id = uuid.uuid4().hex player = PlayerLSL(raw, chunk_size=200, name="real-time-decoding", source_id=source_id) player.start() player.info .. raw:: html
General
MNE object type Info
Measurement date 2002-12-03 at 19:01:10 UTC
Participant Unknown
Experimenter MEG
Acquisition
Sampling frequency 600.61 Hz
Channels
Magnetometers
Gradiometers and
Stimulus
Head & sensor digitization 146 points
Filters
Highpass 0.10 Hz
Lowpass 172.18 Hz
Projections PCA-v1 (off)
PCA-v2 (off)
PCA-v3 (off)


.. GENERATED FROM PYTHON SOURCE LINES 38-44 Signal processing ----------------- We will apply minimal signal processing to the data. First, only the gradiometers will be used for decoding, thus other channels are removed. Then we mark bad channels and applying a low-pass filter at 40 Hz. .. GENERATED FROM PYTHON SOURCE LINES 44-51 .. code-block:: Python stream = StreamLSL(bufsize=5, name="real-time-decoding", source_id=source_id) stream.connect(acquisition_delay=0.1, processing_flags="all") stream.info["bads"] = ["MEG 2443"] stream.pick(("grad", "stim")).filter(None, 40, picks="grad") stream.info .. raw:: html
General
MNE object type Info
Measurement date Unknown
Participant Unknown
Experimenter Unknown
Acquisition
Sampling frequency 600.61 Hz
Channels
Gradiometers and
Stimulus
Head & sensor digitization 146 points
Filters
Highpass 0.10 Hz
Lowpass 172.18 Hz


.. GENERATED FROM PYTHON SOURCE LINES 52-57 Epoch the signal ---------------- Next, we will create epochs around the event ``1`` (audio left) and ``3`` (visual left). .. GENERATED FROM PYTHON SOURCE LINES 57-70 .. code-block:: Python epochs = EpochsStream( stream, bufsize=10, event_id=dict(audio_left=1, visual_left=3), event_channels="STI 014", tmin=-0.2, tmax=0.5, baseline=(None, 0), reject=dict(grad=4000e-13), # unit: T / m (gradiometers) ).connect(acquisition_delay=0.1) epochs.info .. raw:: html
General
MNE object type Info
Measurement date Unknown
Participant Unknown
Experimenter Unknown
Acquisition
Sampling frequency 600.61 Hz
Channels
Gradiometers
Stimulus
Head & sensor digitization 146 points
Filters
Highpass 0.10 Hz
Lowpass 172.18 Hz


.. GENERATED FROM PYTHON SOURCE LINES 71-87 Define the classifier --------------------- We will use a :class:`~sklearn.linear_model.LogisticRegression` classifier to decode the epochs. .. note:: The object :class:`~mne.decoding.Vectorizer` is used to transform the epochs in a 2D array of shape (n_epochs, n_features). It's simply reshapes the epochs data with: .. code-block:: python data = epochs.get_data() data = data.reshape(data.shape[0], -1) .. GENERATED FROM PYTHON SOURCE LINES 87-93 .. code-block:: Python vectorizer = Vectorizer() scaler = StandardScaler() clf = LogisticRegression() classifier = Pipeline([("vector", vectorizer), ("scaler", scaler), ("svm", clf)]) .. GENERATED FROM PYTHON SOURCE LINES 94-100 Decode ------ First, we will wait for a minimum number of epochs to be available. Then, the classifier will be trained for the first time and future epochs will be used to retrain the classifier every 5 epochs. .. GENERATED FROM PYTHON SOURCE LINES 100-151 .. code-block:: Python min_epochs = 10 while epochs.n_new_epochs < min_epochs: time.sleep(0.5) # prepare figure to plot classifiation score if not plt.isinteractive(): plt.ion() fig, ax = plt.subplots() ax.set_xlabel("Epochsn n°") ax.set_ylabel("Classification score (% correct)") ax.set_title("Real-time decoding") ax.set_xlim([min_epochs, 50]) ax.set_ylim([30, 105]) ax.axhline(50, color="k", linestyle="--", label="Chance level") plt.show() # decoding loop scores_x, scores, std_scores = [], [], [] while True: if len(scores_x) != 0 and 50 <= scores_x[-1]: break n_epochs = epochs.n_new_epochs if n_epochs == 0 or n_epochs % 5 != 0: time.sleep(0.5) # give time to the streaming and acquisition threads continue if len(scores_x) == 0: # first training X = epochs.get_data(n_epochs=n_epochs) y = epochs.events[-n_epochs:] else: X = np.concatenate((X, epochs.get_data(n_epochs=n_epochs)), axis=0) y = np.concatenate((y, epochs.events[-n_epochs:])) cv = ShuffleSplit(5, test_size=0.2, random_state=42) scores_t = cross_val_score(classifier, X, y, cv=cv, n_jobs=1) * 100 std_scores.append(scores_t.std()) scores.append(scores_t.mean()) scores_x.append(scores_x[-1] + n_epochs if len(scores_x) != 0 else n_epochs) # update figure ax.plot(scores_x[-2:], scores[-2:], "-x", color="b") hyp_limits = ( np.asarray(scores[-2:]) - np.asarray(std_scores[-2:]), np.asarray(scores[-2:]) + np.asarray(std_scores[-2:]), ) fill = ax.fill_between( scores_x[-2:], y1=hyp_limits[0], y2=hyp_limits[1], color="b", alpha=0.5 ) plt.pause(0.1) plt.draw() .. image-sg:: /generated/examples/images/sphx_glr_40_decode_001.png :alt: Real-time decoding :srcset: /generated/examples/images/sphx_glr_40_decode_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none /home/runner/work/mne-lsl/mne-lsl/examples/40_decode.py:131: RuntimeWarning: The number of epochs requested 15 is greater than the buffer size 10. Selecting the entire buffer. X = np.concatenate((X, epochs.get_data(n_epochs=n_epochs)), axis=0) .. GENERATED FROM PYTHON SOURCE LINES 152-159 Free resources -------------- When you are done with a :class:`~mne_lsl.player.PlayerLSL`, :class:`~mne_lsl.stream.StreamLSL` or :class:`~mne_lsl.stream.EpochsStream`, don't forget to free the resources they use to continuously mock an LSL stream or receive new data from an LSL stream. .. GENERATED FROM PYTHON SOURCE LINES 159-162 .. code-block:: Python epochs.disconnect() .. rst-class:: sphx-glr-script-out .. code-block:: none connected to: .. GENERATED FROM PYTHON SOURCE LINES 163-166 .. code-block:: Python stream.disconnect() .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 167-169 .. code-block:: Python player.stop() .. rst-class:: sphx-glr-script-out .. code-block:: none .. rst-class:: sphx-glr-timing **Total running time of the script:** (1 minutes 19.441 seconds) **Estimated memory usage:** 633 MB .. _sphx_glr_download_generated_examples_40_decode.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: 40_decode.ipynb <40_decode.ipynb>` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: 40_decode.py <40_decode.py>` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: 40_decode.zip <40_decode.zip>` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_