Source code for ieeg.decoding.joint_pca.cross_pt_decoders

""" Cross-Patient Decoder Classes

Author: Zac Spalding
"""

import numpy as np
from sklearn.base import BaseEstimator
from sklearn.decomposition import PCA


[docs] class crossPtDecoder(BaseEstimator): def preprocess_train(self, X, y=None): pass def preprocess_test(self, X, y=None): pass def fit(self, X, y, **kwargs): X_p, y_p = self.preprocess_train(X, y, **kwargs) return self.decoder.fit(X_p, y_p) def predict(self, X): X_p = self.preprocess_test(X) return self.decoder.predict(X_p) def score(self, X, y, **kwargs): X_p = self.preprocess_test(X) return self.decoder.score(X_p, y, **kwargs)
[docs] class crossPtDecoder_sepDimRed(crossPtDecoder): """ Cross-Patient Decoder with separate PCA for each patient. """ def __init__(self, cross_pt_data, decoder, dim_red=PCA, n_comp=10): self.cross_pt_data = cross_pt_data self.decoder = decoder self.dim_red = dim_red self.n_comp = n_comp def preprocess_train(self, X, y, **kwargs): cross_pt_trials = [x.shape[0] for x, _, _ in self.cross_pt_data] # reshape features to be 2D (preserve last dimension for reduction) X_cross_r = [x.reshape(-1, x.shape[-1]) for x, _, _ in self.cross_pt_data] X_tar_r = X.reshape(-1, X.shape[-1]) # reduce dimensionality of cross-patient data X_cross_dr = [self.dim_red(n_components=self.n_comp).fit_transform(x) for x in X_cross_r] # reduce dimensionality of target data, saving dim. red. object for # test set tar_dr = self.dim_red(n_components=self.n_comp) X_tar_dr = tar_dr.fit_transform(X_tar_r) self.tar_dr = tar_dr # reshape for concatenation X_cross_dr = [x.reshape(cross_pt_trials[i], -1, x.shape[-1]) for i, x in enumerate(X_cross_dr)] X_cross_dr = [x.reshape(x.shape[0], -1) for x in X_cross_dr] X_tar_dr = X_tar_dr.reshape(X.shape[0], -1) # concatenate cross-patient data X_dr = np.vstack([X_tar_dr] + X_cross_dr) y_dr = np.hstack([y] + [y for _, y, _ in self.cross_pt_data]) return X_dr, y_dr def preprocess_test(self, X): X_r = X.reshape(-1, X.shape[-1]) X_dr = self.tar_dr.transform(X_r) return X_dr.reshape(X.shape[0], -1)
[docs] class crossPtDecoder_sepAlign(crossPtDecoder): def __init__(self, cross_pt_data, decoder, aligner, dim_red=PCA, n_comp=10): self.cross_pt_data = cross_pt_data self.decoder = decoder self.dim_red = dim_red self.n_comp = n_comp self.aligner = aligner def preprocess_train(self, X, y, y_align=None): cross_pt_trials = [x.shape[0] for x, _, _ in self.cross_pt_data] tar_trials = X.shape[0] # reshape features to be 2D (preserve last dimension for reduction) X_cross_r = [x.reshape(-1, x.shape[-1]) for x, _, _ in self.cross_pt_data] X_tar_r = X.reshape(-1, X.shape[-1]) # reduce dimensionality of cross-patient data X_cross_dr = [self.dim_red(n_components=self.n_comp).fit_transform(x) for x in X_cross_r] # reduce dimensionality of target data, saving dim. red. object for # test set tar_dr = self.dim_red(n_components=self.n_comp) X_tar_dr = tar_dr.fit_transform(X_tar_r) self.tar_dr = tar_dr # reshape back to 3D X_cross_dr = [x.reshape(cross_pt_trials[i], -1, x.shape[-1]) for i, x in enumerate(X_cross_dr)] X_tar_dr = X_tar_dr.reshape(X.shape[0], -1, X_tar_dr.shape[-1]) # option for separate alignment labels if y_align is None: y_align = y y_align_cross = [y_a for _, _, y_a in self.cross_pt_data] algns = [self.aligner() for _ in range(len(self.cross_pt_data))] X_algn_dr = [] for i, algn in enumerate(algns): algn.fit(X_tar_dr, X_cross_dr[i], y_align, y_align_cross[i]) X_algn_dr.append(algn.transform(X_cross_dr[i])) X_algn_dr = [x.reshape(x.shape[0], -1) for x in X_algn_dr] X_tar_dr = X_tar_dr.reshape(X_tar_dr.shape[0], -1) # concatenate cross-patient data X_pool = np.vstack([X_tar_dr] + X_algn_dr) y_pool = np.hstack([y] + [y for _, y, _ in self.cross_pt_data]) return X_pool, y_pool def preprocess_test(self, X): X_r = X.reshape(-1, X.shape[-1]) X_dr = self.tar_dr.transform(X_r) return X_dr.reshape(X.shape[0], -1)