Joint pca decoding

Takes high gamma filtered data with event labels from multiple subjects and performs joint pca decoding

from ieeg.navigate import channel_outlier_marker, trial_ieeg
from ieeg.io import raw_from_layout
from ieeg.timefreq.utils import crop_pad
from ieeg.timefreq import gamma
from ieeg.decoding.joint_pca.alignment_methods import JointPCADecomp
from ieeg.arrays.label import Labels
from bids import BIDSLayout
import mne
import numpy as np
from sklearn.model_selection import KFold
from sklearn.svm import SVC
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

Load Data

bids_root = mne.datasets.epilepsy_ecog.data_path()
layout = BIDSLayout(bids_root)
raw = raw_from_layout(layout,
                      subject="pt1",
                      preload=True,
                      extension=".vhdr")
Extracting parameters from /home/docs/mne_data/MNE-epilepsy-ecog-data/sub-pt1/ses-presurgery/ieeg/sub-pt1_ses-presurgery_task-ictal_ieeg.vhdr...
Setting channel info structure...
Reading events from /home/docs/mne_data/MNE-epilepsy-ecog-data/sub-pt1/ses-presurgery/ieeg/sub-pt1_ses-presurgery_task-ictal_events.tsv.
Reading channel info from /home/docs/mne_data/MNE-epilepsy-ecog-data/sub-pt1/ses-presurgery/ieeg/sub-pt1_ses-presurgery_task-ictal_channels.tsv.
Reading electrode coords from /home/docs/mne_data/MNE-epilepsy-ecog-data/sub-pt1/ses-presurgery/ieeg/sub-pt1_ses-presurgery_space-fsaverage_electrodes.tsv.
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/checkouts/latest/ieeg/io.py:283: RuntimeWarning: DigMontage is only a subset of info. There are 3 channel positions not present in the DigMontage. The channels missing from the montage are:

['RQ1', 'RQ2', 'N/A'].

Consider using inst.rename_channels to match the montage nomenclature, or inst.set_channel_types if these are not EEG channels, or use the on_missing parameter if the channel positions are allowed to be unknown in your analyses.
  whole_raw = read_raw_bids(bids_path=BIDS_path, verbose=verbose)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/checkouts/latest/ieeg/io.py:283: RuntimeWarning: Unable to map the following column(s) to to MNE:
outcome: S
engel_score: 1.0
ilae_score: 2.0
date_follow_up: n/a
ethnicity: 0.0
years_follow_up: 3.0
site: NIH
clinical_complexity: 1.0
  whole_raw = read_raw_bids(bids_path=BIDS_path, verbose=verbose)
Reading 0 ... 269079  =      0.000 ...   269.079 secs...

Preprocessing

# Mark channel outliers as bad
channel_outlier_marker(raw, 5)

# Exclude bad channels, then load the good channels into memory
raw.drop_channels(raw.info['bads'])
good = raw.copy()
good.load_data()

# Remove intermediates for memory efficiency
del raw

# CAR (common average reference)
good.set_eeg_reference()
outlier round 1 channels: ['N/A']
outlier round 2 channels: ['N/A', 'RQ2']
outlier round 3 channels: ['N/A', 'RQ2', 'AST2']
outlier round 4 channels: ['N/A', 'RQ2', 'AST2', 'AD3']
outlier round 5 channels: ['N/A', 'RQ2', 'AST2', 'AD3', 'PD4']
outlier round 6 channels: ['N/A', 'RQ2', 'AST2', 'AD3', 'PD4', 'G32']
ECoG channel type selected for re-referencing
Applying average reference.
Applying a custom ('ECoG',) reference.
General
Filename(s) sub-pt1_ses-presurgery_task-ictal_ieeg.eeg
MNE object type RawBrainVision
Measurement date 1920-07-24 at 19:35:19 UTC
Participant sub-pt1
Experimenter mne_anonymize
Acquisition
Duration 00:04:30 (HH:MM:SS)
Sampling frequency 1000.00 Hz
Time points 269,080
Channels
misc
ECoG
Head & sensor digitization 86 points
Filters
Highpass 0.00 Hz
Lowpass 500.00 Hz


High Gamma Filter

# extract the epochs of interest
ev1 = trial_ieeg(good, ["PD", "G16", "SLT1-3"], (-1, 2), preload=True)
base = trial_ieeg(good, "onset", (-1, 0.5), preload=True)

# extract high gamma power
gamma.extract(ev1, copy=False, n_jobs=1)
gamma.extract(base, copy=False, n_jobs=1)

# trim 0.5 seconds on the beginning and end of the data (edge artifacts)
crop_pad(ev1, "500ms")
crop_pad(base, "500ms")

# remove intermediates
del good
Used Annotations descriptions: [np.str_('AD1-4, ATT1,2'), np.str_('AST1,3'), np.str_('G16'), np.str_('PD'), np.str_('SLT1-3'), np.str_('offset'), np.str_('onset')]
Not setting metadata
3 matching events found
No baseline correction applied
0 projection items activated
Using data from preloaded Raw for 3 events and 3001 original time points ...
0 bad epochs dropped
Used Annotations descriptions: [np.str_('AD1-4, ATT1,2'), np.str_('AST1,3'), np.str_('G16'), np.str_('PD'), np.str_('SLT1-3'), np.str_('offset'), np.str_('onset')]
Not setting metadata
1 matching events found
No baseline correction applied
0 projection items activated
Using data from preloaded Raw for 1 events and 1501 original time points ...
0 bad epochs dropped

  0%|          | 0/3 [00:00<?, ?it/s]
 67%|██████▋   | 2/3 [00:00<00:00, 19.90it/s]
100%|██████████| 3/3 [00:00<00:00, 20.61it/s]

  0%|          | 0/1 [00:00<?, ?it/s]
100%|██████████| 1/1 [00:00<00:00, 40.70it/s]

Extract data and labels

data1 = ev1.get_data(slice(0, 40)).swapaxes(1, 2)
data2 = ev1.get_data(slice(41, 81)).swapaxes(1, 2)
phon_labels = Labels([["AD1-4, ATT1,2", "PD", "G16"]]).T

Decoding

n_iter = 5
cv = KFold(n_splits=3, shuffle=True, random_state=42)
y_true_all, y_pred_all = [], []
for _ in range(n_iter):  # repeat K-fold evaluation n_iter times
    for train_idx, test_idx in cv.split(data1, phon_labels):
        # split X1 into train and test
        X1_train, y1_train = data1[train_idx], phon_labels[train_idx]
        X1_test, y1_test = data1[test_idx], phon_labels[test_idx]
        # X2, y2 are only pooled with X1 for training, so no splitting
        X2, y2 = data2, phon_labels

        # Jointly decompose X1 and X2 to shared subspace via PCA
        jointPCA = JointPCADecomp(n_components=3)
        X1_train, X2 = jointPCA.fit_transform([X1_train, X2], [y1_train, y2])
        # Transform X1_test to shared subspace
        X1_test = jointPCA.transform(X1_test, idx=0)

        # Reshape data into trials x features (features = PCs*time)
        X1_train = X1_train.reshape(X1_train.shape[0], -1)
        X1_test = X1_test.reshape(X1_test.shape[0], -1)
        X2 = X2.reshape(X2.shape[0], -1)

        # pool X1 training data and X2 data together
        X_train = np.concatenate((X1_train, X2), axis=0)
        y_train = np.concatenate((y1_train, y2), axis=0)

        # classification via a linear SVM
        clf = SVC(kernel='linear')
        clf.fit(X_train, y_train)
        y_pred = clf.predict(X1_test)

        # store results
        y_true_all.extend(y1_test)
        y_pred_all.extend(y_pred)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)
/home/docs/checkouts/readthedocs.org/user_builds/ieeg-pipelines/conda/latest/lib/python3.12/site-packages/sklearn/utils/validation.py:1406: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  y = column_or_1d(y, warn=True)

Results - Confusion Matrix

plot load zac
<sklearn.metrics._plot.confusion_matrix.ConfusionMatrixDisplay object at 0x74b0aa89b350>

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

Estimated memory usage: 1271 MB

Related examples

PCA-LDA Decoding

PCA-LDA Decoding

High Gamma Filter

High Gamma Filter

Time Permutation Cluster Statistics

Time Permutation Cluster Statistics

Gallery generated by Sphinx-Gallery