Decoding sensor space data with generalization across time and conditions#

This example runs the analysis described in [1]. It illustrates how one can fit a linear classifier to identify a discriminatory topography at a given time instant and subsequently assess whether this linear model can accurately predict all of the time samples of a second set of conditions.

# Authors: Jean-Rémi King <jeanremi.king@gmail.com>
#          Alexandre Gramfort <alexandre.gramfort@inria.fr>
#          Denis Engemann <denis.engemann@gmail.com>
#
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

import mne
from mne.datasets import sample
from mne.decoding import GeneralizingEstimator

print(__doc__)

# Preprocess data
data_path = sample.data_path()
# Load and filter data, set up epochs
meg_path = data_path / "MEG" / "sample"
raw_fname = meg_path / "sample_audvis_filt-0-40_raw.fif"
events_fname = meg_path / "sample_audvis_filt-0-40_raw-eve.fif"
raw = mne.io.read_raw_fif(raw_fname, preload=True)
picks = mne.pick_types(raw.info, meg=True, exclude="bads")  # Pick MEG channels
raw.filter(1.0, 30.0, fir_design="firwin")  # Band pass filtering signals
events = mne.read_events(events_fname)
event_id = {
    "Auditory/Left": 1,
    "Auditory/Right": 2,
    "Visual/Left": 3,
    "Visual/Right": 4,
}
tmin = -0.050
tmax = 0.400
# decimate to make the example faster to run, but then use verbose='error' in
# the Epochs constructor to suppress warning about decimation causing aliasing
decim = 2
epochs = mne.Epochs(
    raw,
    events,
    event_id=event_id,
    tmin=tmin,
    tmax=tmax,
    proj=True,
    picks=picks,
    baseline=None,
    preload=True,
    reject=dict(mag=5e-12),
    decim=decim,
    verbose="error",
)
Opening raw data file /home/circleci/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif...
    Read a total of 4 projection items:
        PCA-v1 (1 x 102)  idle
        PCA-v2 (1 x 102)  idle
        PCA-v3 (1 x 102)  idle
        Average EEG reference (1 x 60)  idle
    Range : 6450 ... 48149 =     42.956 ...   320.665 secs
Ready.
Reading 0 ... 41699  =      0.000 ...   277.709 secs...
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 1.00
- Lower transition bandwidth: 1.00 Hz (-6 dB cutoff frequency: 0.50 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 497 samples (3.310 s)

[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.0s
[Parallel(n_jobs=1)]: Done  71 tasks      | elapsed:    0.1s
[Parallel(n_jobs=1)]: Done 161 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done 287 tasks      | elapsed:    0.5s

We will train the classifier on all left visual vs auditory trials and test on all right visual vs auditory trials.

clf = make_pipeline(
    StandardScaler(),
    LogisticRegression(solver="liblinear"),  # liblinear is faster than lbfgs
)
time_gen = GeneralizingEstimator(clf, scoring="roc_auc", n_jobs=None, verbose=True)

# Fit classifiers on the epochs where the stimulus was presented to the left.
# Note that the experimental condition y indicates auditory or visual
time_gen.fit(X=epochs["Left"].get_data(copy=False), y=epochs["Left"].events[:, 2] > 2)
  0%|          | Fitting GeneralizingEstimator : 0/35 [00:00<?,       ?it/s]
  6%|▌         | Fitting GeneralizingEstimator : 2/35 [00:00<00:00,   57.28it/s]
 11%|█▏        | Fitting GeneralizingEstimator : 4/35 [00:00<00:00,   58.29it/s]
 17%|█▋        | Fitting GeneralizingEstimator : 6/35 [00:00<00:00,   58.66it/s]
 26%|██▌       | Fitting GeneralizingEstimator : 9/35 [00:00<00:00,   66.80it/s]
 34%|███▍      | Fitting GeneralizingEstimator : 12/35 [00:00<00:00,   71.12it/s]
 43%|████▎     | Fitting GeneralizingEstimator : 15/35 [00:00<00:00,   74.48it/s]
 51%|█████▏    | Fitting GeneralizingEstimator : 18/35 [00:00<00:00,   76.88it/s]
 60%|██████    | Fitting GeneralizingEstimator : 21/35 [00:00<00:00,   78.68it/s]
 69%|██████▊   | Fitting GeneralizingEstimator : 24/35 [00:00<00:00,   79.53it/s]
 74%|███████▍  | Fitting GeneralizingEstimator : 26/35 [00:00<00:00,   77.06it/s]
 83%|████████▎ | Fitting GeneralizingEstimator : 29/35 [00:00<00:00,   78.44it/s]
 89%|████████▊ | Fitting GeneralizingEstimator : 31/35 [00:00<00:00,   76.38it/s]
 97%|█████████▋| Fitting GeneralizingEstimator : 34/35 [00:00<00:00,   77.68it/s]
100%|██████████| Fitting GeneralizingEstimator : 35/35 [00:00<00:00,   77.69it/s]
<GeneralizingEstimator(base_estimator=Pipeline(steps=[('standardscaler', StandardScaler()), ('logisticregression', LogisticRegression(solver='liblinear'))]), scoring='roc_auc', verbose=True, fitted with 35 estimators, fitted with 35 estimators>
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


Score on the epochs where the stimulus was presented to the right.

scores = time_gen.score(
    X=epochs["Right"].get_data(copy=False), y=epochs["Right"].events[:, 2] > 2
)
  0%|          | Scoring GeneralizingEstimator : 0/1225 [00:00<?,       ?it/s]
  1%|          | Scoring GeneralizingEstimator : 12/1225 [00:00<00:03,  338.35it/s]
  2%|▏         | Scoring GeneralizingEstimator : 25/1225 [00:00<00:03,  360.71it/s]
  3%|▎         | Scoring GeneralizingEstimator : 37/1225 [00:00<00:03,  359.20it/s]
  4%|▍         | Scoring GeneralizingEstimator : 50/1225 [00:00<00:03,  366.39it/s]
  5%|▌         | Scoring GeneralizingEstimator : 63/1225 [00:00<00:03,  364.35it/s]
  6%|▌         | Scoring GeneralizingEstimator : 76/1225 [00:00<00:03,  368.38it/s]
  7%|▋         | Scoring GeneralizingEstimator : 89/1225 [00:00<00:03,  371.10it/s]
  8%|▊         | Scoring GeneralizingEstimator : 101/1225 [00:00<00:03,  368.81it/s]
  9%|▉         | Scoring GeneralizingEstimator : 114/1225 [00:00<00:02,  370.71it/s]
 10%|█         | Scoring GeneralizingEstimator : 127/1225 [00:00<00:02,  372.50it/s]
 11%|█▏        | Scoring GeneralizingEstimator : 139/1225 [00:00<00:02,  370.49it/s]
 12%|█▏        | Scoring GeneralizingEstimator : 152/1225 [00:00<00:02,  371.94it/s]
 13%|█▎        | Scoring GeneralizingEstimator : 165/1225 [00:00<00:02,  371.28it/s]
 14%|█▍        | Scoring GeneralizingEstimator : 177/1225 [00:00<00:02,  369.85it/s]
 16%|█▌        | Scoring GeneralizingEstimator : 190/1225 [00:00<00:02,  371.26it/s]
 17%|█▋        | Scoring GeneralizingEstimator : 203/1225 [00:00<00:02,  372.24it/s]
 18%|█▊        | Scoring GeneralizingEstimator : 215/1225 [00:00<00:02,  370.90it/s]
 19%|█▊        | Scoring GeneralizingEstimator : 228/1225 [00:00<00:02,  372.15it/s]
 20%|█▉        | Scoring GeneralizingEstimator : 241/1225 [00:00<00:02,  371.06it/s]
 21%|██        | Scoring GeneralizingEstimator : 254/1225 [00:00<00:02,  372.06it/s]
 22%|██▏       | Scoring GeneralizingEstimator : 267/1225 [00:00<00:02,  373.10it/s]
 23%|██▎       | Scoring GeneralizingEstimator : 280/1225 [00:00<00:02,  373.74it/s]
 24%|██▍       | Scoring GeneralizingEstimator : 292/1225 [00:00<00:02,  372.44it/s]
 25%|██▍       | Scoring GeneralizingEstimator : 305/1225 [00:00<00:02,  373.39it/s]
 26%|██▌       | Scoring GeneralizingEstimator : 317/1225 [00:00<00:02,  372.18it/s]
 27%|██▋       | Scoring GeneralizingEstimator : 330/1225 [00:00<00:02,  373.12it/s]
 28%|██▊       | Scoring GeneralizingEstimator : 342/1225 [00:00<00:02,  371.97it/s]
 29%|██▉       | Scoring GeneralizingEstimator : 355/1225 [00:00<00:02,  372.59it/s]
 30%|███       | Scoring GeneralizingEstimator : 368/1225 [00:00<00:02,  372.72it/s]
 31%|███       | Scoring GeneralizingEstimator : 380/1225 [00:01<00:02,  371.66it/s]
 32%|███▏      | Scoring GeneralizingEstimator : 393/1225 [00:01<00:02,  372.55it/s]
 33%|███▎      | Scoring GeneralizingEstimator : 406/1225 [00:01<00:02,  371.99it/s]
 34%|███▍      | Scoring GeneralizingEstimator : 419/1225 [00:01<00:02,  372.85it/s]
 35%|███▌      | Scoring GeneralizingEstimator : 431/1225 [00:01<00:02,  371.84it/s]
 36%|███▌      | Scoring GeneralizingEstimator : 444/1225 [00:01<00:02,  372.70it/s]
 37%|███▋      | Scoring GeneralizingEstimator : 456/1225 [00:01<00:02,  371.71it/s]
 38%|███▊      | Scoring GeneralizingEstimator : 469/1225 [00:01<00:02,  372.49it/s]
 39%|███▉      | Scoring GeneralizingEstimator : 482/1225 [00:01<00:01,  373.14it/s]
 40%|████      | Scoring GeneralizingEstimator : 495/1225 [00:01<00:01,  373.31it/s]
 41%|████▏     | Scoring GeneralizingEstimator : 507/1225 [00:01<00:01,  372.30it/s]
 42%|████▏     | Scoring GeneralizingEstimator : 520/1225 [00:01<00:01,  373.05it/s]
 44%|████▎     | Scoring GeneralizingEstimator : 533/1225 [00:01<00:01,  373.23it/s]
 44%|████▍     | Scoring GeneralizingEstimator : 545/1225 [00:01<00:01,  372.27it/s]
 46%|████▌     | Scoring GeneralizingEstimator : 558/1225 [00:01<00:01,  372.95it/s]
 47%|████▋     | Scoring GeneralizingEstimator : 571/1225 [00:01<00:01,  372.64it/s]
 48%|████▊     | Scoring GeneralizingEstimator : 584/1225 [00:01<00:01,  373.33it/s]
 49%|████▊     | Scoring GeneralizingEstimator : 596/1225 [00:01<00:01,  372.36it/s]
 50%|████▉     | Scoring GeneralizingEstimator : 609/1225 [00:01<00:01,  373.11it/s]
 51%|█████     | Scoring GeneralizingEstimator : 621/1225 [00:01<00:01,  372.17it/s]
 52%|█████▏    | Scoring GeneralizingEstimator : 634/1225 [00:01<00:01,  372.90it/s]
 53%|█████▎    | Scoring GeneralizingEstimator : 648/1225 [00:01<00:01,  373.46it/s]
 54%|█████▍    | Scoring GeneralizingEstimator : 660/1225 [00:01<00:01,  372.48it/s]
 55%|█████▍    | Scoring GeneralizingEstimator : 673/1225 [00:01<00:01,  373.09it/s]
 56%|█████▌    | Scoring GeneralizingEstimator : 685/1225 [00:01<00:01,  372.17it/s]
 57%|█████▋    | Scoring GeneralizingEstimator : 698/1225 [00:01<00:01,  372.88it/s]
 58%|█████▊    | Scoring GeneralizingEstimator : 710/1225 [00:01<00:01,  371.97it/s]
 59%|█████▉    | Scoring GeneralizingEstimator : 723/1225 [00:01<00:01,  372.70it/s]
 60%|██████    | Scoring GeneralizingEstimator : 735/1225 [00:01<00:01,  371.79it/s]
 61%|██████    | Scoring GeneralizingEstimator : 748/1225 [00:02<00:01,  372.50it/s]
 62%|██████▏   | Scoring GeneralizingEstimator : 761/1225 [00:02<00:01,  372.53it/s]
 63%|██████▎   | Scoring GeneralizingEstimator : 773/1225 [00:02<00:01,  371.67it/s]
 64%|██████▍   | Scoring GeneralizingEstimator : 786/1225 [00:02<00:01,  372.16it/s]
 65%|██████▌   | Scoring GeneralizingEstimator : 799/1225 [00:02<00:01,  372.85it/s]
 66%|██████▌   | Scoring GeneralizingEstimator : 811/1225 [00:02<00:01,  371.72it/s]
 67%|██████▋   | Scoring GeneralizingEstimator : 824/1225 [00:02<00:01,  372.45it/s]
 68%|██████▊   | Scoring GeneralizingEstimator : 837/1225 [00:02<00:01,  372.05it/s]
 69%|██████▉   | Scoring GeneralizingEstimator : 850/1225 [00:02<00:01,  372.71it/s]
 70%|███████   | Scoring GeneralizingEstimator : 862/1225 [00:02<00:00,  371.83it/s]
 71%|███████▏  | Scoring GeneralizingEstimator : 875/1225 [00:02<00:00,  372.54it/s]
 72%|███████▏  | Scoring GeneralizingEstimator : 887/1225 [00:02<00:00,  371.72it/s]
 73%|███████▎  | Scoring GeneralizingEstimator : 899/1225 [00:02<00:00,  370.91it/s]
 74%|███████▍  | Scoring GeneralizingEstimator : 912/1225 [00:02<00:00,  371.24it/s]
 76%|███████▌  | Scoring GeneralizingEstimator : 925/1225 [00:02<00:00,  371.99it/s]
 77%|███████▋  | Scoring GeneralizingEstimator : 938/1225 [00:02<00:00,  371.87it/s]
 78%|███████▊  | Scoring GeneralizingEstimator : 951/1225 [00:02<00:00,  372.38it/s]
 79%|███████▊  | Scoring GeneralizingEstimator : 963/1225 [00:02<00:00,  371.50it/s]
 80%|███████▉  | Scoring GeneralizingEstimator : 976/1225 [00:02<00:00,  372.22it/s]
 81%|████████  | Scoring GeneralizingEstimator : 988/1225 [00:02<00:00,  371.39it/s]
 82%|████████▏ | Scoring GeneralizingEstimator : 1001/1225 [00:02<00:00,  372.08it/s]
 83%|████████▎ | Scoring GeneralizingEstimator : 1014/1225 [00:02<00:00,  372.41it/s]
 84%|████████▍ | Scoring GeneralizingEstimator : 1026/1225 [00:02<00:00,  371.56it/s]
 85%|████████▍ | Scoring GeneralizingEstimator : 1039/1225 [00:02<00:00,  371.08it/s]
 86%|████████▌ | Scoring GeneralizingEstimator : 1052/1225 [00:02<00:00,  371.75it/s]
 87%|████████▋ | Scoring GeneralizingEstimator : 1065/1225 [00:02<00:00,  372.45it/s]
 88%|████████▊ | Scoring GeneralizingEstimator : 1077/1225 [00:02<00:00,  371.64it/s]
 89%|████████▉ | Scoring GeneralizingEstimator : 1090/1225 [00:02<00:00,  372.24it/s]
 90%|█████████ | Scoring GeneralizingEstimator : 1103/1225 [00:02<00:00,  372.86it/s]
 91%|█████████ | Scoring GeneralizingEstimator : 1115/1225 [00:02<00:00,  372.01it/s]
 92%|█████████▏| Scoring GeneralizingEstimator : 1128/1225 [00:03<00:00,  371.80it/s]
 93%|█████████▎| Scoring GeneralizingEstimator : 1140/1225 [00:03<00:00,  370.98it/s]
 94%|█████████▍| Scoring GeneralizingEstimator : 1153/1225 [00:03<00:00,  371.73it/s]
 95%|█████████▌| Scoring GeneralizingEstimator : 1166/1225 [00:03<00:00,  372.28it/s]
 96%|█████████▌| Scoring GeneralizingEstimator : 1178/1225 [00:03<00:00,  371.48it/s]
 97%|█████████▋| Scoring GeneralizingEstimator : 1191/1225 [00:03<00:00,  372.18it/s]
 98%|█████████▊| Scoring GeneralizingEstimator : 1204/1225 [00:03<00:00,  372.03it/s]
 99%|█████████▉| Scoring GeneralizingEstimator : 1217/1225 [00:03<00:00,  372.71it/s]
100%|██████████| Scoring GeneralizingEstimator : 1225/1225 [00:03<00:00,  372.79it/s]
100%|██████████| Scoring GeneralizingEstimator : 1225/1225 [00:03<00:00,  372.18it/s]

Plot

fig, ax = plt.subplots(layout="constrained")
im = ax.matshow(
    scores,
    vmin=0,
    vmax=1.0,
    cmap="RdBu_r",
    origin="lower",
    extent=epochs.times[[0, -1, 0, -1]],
)
ax.axhline(0.0, color="k")
ax.axvline(0.0, color="k")
ax.xaxis.set_ticks_position("bottom")
ax.set_xlabel(
    'Condition: "Right"\nTesting Time (s)',
)
ax.set_ylabel('Condition: "Left"\nTraining Time (s)')
ax.set_title("Generalization across time and condition", fontweight="bold")
fig.colorbar(im, ax=ax, label="Performance (ROC AUC)")
plt.show()
Generalization across time and condition

References#

Total running time of the script: (0 minutes 6.524 seconds)

Gallery generated by Sphinx-Gallery