import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
from scipy.signal import firls, filtfilt
from scipy.fftpack import fft, ifft
from mne import create_info, EvokedArray
from mne.channels import make_dig_montage
Chapter 23
Chapter 23
Analyzing Neural Time Series Data
Python code for Chapter 23 – converted from original Matlab by AE Studio (and ChatGPT)
Original Matlab code by Mike X Cohen
This code accompanies the book, titled “Analyzing Neural Time Series Data” (MIT Press).
Using the code without following the book may lead to confusion, incorrect data analyses, and misinterpretations of results.
Mike X Cohen and AE Studio assume no responsibility for inappropriate or incorrect use of this code.
Import necessary libraries
Figure 23.2
# Load sample data
= loadmat('../data/sampleEEGdata.mat')['EEG'][0, 0]
EEG
# Compute ERP
= np.mean(EEG['data'], axis=2)
erp
# Subtract mean and compute covariance
= erp - np.mean(erp, axis=1, keepdims=True)
erp = np.dot(erp, erp.T) / (EEG['pnts'][0][0] - 1)
covar
=(12, 4))
plt.figure(figsize# Plot covariance of ERP
131)
plt.subplot(='equal', clim=[-1, 5])
plt.imshow(covar, aspect= [20, 40, 60]
xticks = [10, 20, 30, 40, 50, 60]
yticks
plt.xticks(xticks)
plt.yticks(yticks)'chanlocs'][0]['labels'][tick-1][0] for tick in xticks])
plt.gca().set_xticklabels([EEG['chanlocs'][0]['labels'][tick-1][0] for tick in yticks])
plt.gca().set_yticklabels([EEG['Covariance of ERP')
plt.title(
# One covariance of all timepoints
= np.reshape(EEG['data'], (EEG['nbchan'][0][0], EEG['pnts'][0][0] * EEG['trials'][0][0]), 'F')
eeg = eeg - np.mean(eeg, axis=1, keepdims=True)
eeg = np.dot(eeg, eeg.T) / (len(eeg) - 1)
covar
# Plot covariance of single-trial EEG
132)
plt.subplot(='equal', clim=[20000, 150000])
plt.imshow(covar, aspect= [20, 40, 60]
xticks = [10, 20, 30, 40, 50, 60]
yticks
plt.xticks(xticks)
plt.yticks(yticks)'chanlocs'][0]['labels'][tick-1][0] for tick in xticks])
plt.gca().set_xticklabels([EEG['chanlocs'][0]['labels'][tick-1][0] for tick in yticks])
plt.gca().set_yticklabels([EEG['Covariance of single-trial EEG')
plt.title(
# Average single-trial covariances
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(EEG['trials'][0][0]):
= EEG['data'][:, :, i] - np.mean(EEG['data'][:, :, i], axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= i
covar
# Plot average covariance of single-trial EEG
133)
plt.subplot(='equal', clim=[20, 150])
plt.imshow(covar, aspect= [20, 40, 60]
xticks = [10, 20, 30, 40, 50, 60]
yticks
plt.xticks(xticks)
plt.yticks(yticks)'chanlocs'][0]['labels'][tick-1][0] for tick in xticks])
plt.gca().set_xticklabels([EEG['chanlocs'][0]['labels'][tick-1][0] for tick in yticks])
plt.gca().set_yticklabels([EEG['Average covariance of single-trial EEG')
plt.title(
plt.tight_layout() plt.show()
Figure 23.3
# Compute covariance of ERP
= np.mean(EEG['data'], axis=2)
erp = erp - np.mean(erp, axis=1, keepdims=True)
erp = np.dot(erp, erp.T) / (EEG['pnts'][0][0] - 1)
covar
# PCA via eigenvalue decomposition
= np.linalg.eig(covar)
eigvals, pc
= pc[:, np.argsort(-eigvals)]
pc = np.sort(eigvals)[::-1]
eigvals = 100 * eigvals / np.sum(eigvals) # Convert to percent change
eigvals
# create channel montage
= [chan['labels'][0] for chan in EEG['chanlocs'][0]]
chan_labels = np.vstack([-1*EEG['chanlocs']['Y'], EEG['chanlocs']['X'], EEG['chanlocs']['Z']]).T
coords # remove channels outside of head
= np.where(np.array([chan['radius'][0][0] for chan in EEG['chanlocs'][0]]) > 0.54)
exclude_chans = np.delete(coords, exclude_chans, axis=0)
coords = np.delete(chan_labels, exclude_chans)
chan_labels = np.delete(pc, exclude_chans, axis=0)
pc_lp = make_dig_montage(ch_pos=dict(zip(chan_labels, coords)), coord_frame='head')
montage
# create MNE Info and Evoked object
= create_info(list(chan_labels), EEG['srate'], ch_types='eeg')
info
# Plot the first 9 principal components
for i in range(9):
102, figsize=(10, 10))
plt.figure(= plt.subplot(3, 3, i + 1)
ax = EvokedArray(pc_lp[:, i, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=False, sphere=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', axes=ax, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensorsf'PC #{i + 1}, eigval={eigvals[i]:.4f}')
plt.title(
101, figsize=(10, 10))
plt.figure(9, 1, i + 1)
plt.subplot('times'][0], pc[:, i].T @ erp)
plt.plot(EEG[0, color='k')
plt.axhline(-200, 1200])
plt.xlim([f'PC #{i + 1}, eigval={eigvals[i]:.4f}')
plt.title(
plt.tight_layout()
plt.show()
# Average single-trial covariances
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(EEG['trials'][0][0]):
= EEG['data'][:, :, i] - np.mean(EEG['data'][:, :, i], axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= EEG['trials'][0][0]
covar
# PCA via eigenvalue decomposition
= np.linalg.eig(covar)
eigvals, pc
= pc[:, np.argsort(-eigvals)]
pc = np.sort(eigvals)[::-1]
eigvals = 100 * eigvals / np.sum(eigvals) # Convert to percent change
eigvals
= np.delete(pc, exclude_chans, axis=0)
pc_lp
# Plot the first 9 principal components
for i in range(9):
10, figsize=(10, 10))
plt.figure(= plt.subplot(3, 3, i + 1)
ax = EvokedArray(pc_lp[:, i, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=False, sphere=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', axes=ax, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensorsf'PC #{i + 1}, eigval={eigvals[i]:.4f}')
plt.title(
11, figsize=(10, 10))
plt.figure(= plt.subplot(9, 1, i + 1)
ax = np.zeros(EEG['pnts'][0][0])
pctimes for triali in range(EEG['trials'][0][0]):
= EEG['data'][:, :, triali] - np.mean(EEG['data'][:, :, triali], axis=1, keepdims=True)
eeg += pc[:, i].T @ eeg
pctimes 'times'][0], pctimes / EEG['trials'][0][0])
plt.plot(EEG[0, color='k')
plt.axhline(-200, 1200])
plt.xlim([f'PC #{i + 1}, eigval={eigvals[i]:.4f}')
plt.title(
plt.tight_layout() plt.show()
Tangent
An easily made mistake is to confuse the dimension order of the PC matrix. To be sure you have the correct orientation, plot the first component; it should have a spatially broad ERP-like distribution.
# create channel montage
= [chan['labels'][0] for chan in EEG['chanlocs'][0]]
chan_labels = np.vstack([-1*EEG['chanlocs']['Y'], EEG['chanlocs']['X'], EEG['chanlocs']['Z']]).T
coords = make_dig_montage(ch_pos=dict(zip(chan_labels, coords)), coord_frame='head')
montage
# create MNE Info and Evoked object
= create_info(list(chan_labels), EEG['srate'], ch_types='eeg')
info
# Plot the first principal component to ensure correct orientation
= plt.subplots(1, 2, figsize=(10, 5))
fig, axs = EvokedArray(pc[:, 0, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', axes=axs[0], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere0].set_title('Correct orientation!')
axs[
= EvokedArray(pc[0, :, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', axes=axs[1], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere1].set_title('Incorrect orientation!')
axs[
plt.tight_layout() plt.show()
Figure 23.4
= 2 # 1 for panel A; 2 for panel B
pcanum
# Average single-trial covariances
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(EEG['trials'][0][0]):
= EEG['data'][:, :, i] - np.mean(EEG['data'][:, :, i], axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= EEG['trials'][0][0]
covar
# PCA via eigenvalue decomposition
= np.linalg.eig(covar)
eigvals, pc
= pc[:, np.argsort(-eigvals)]
pc = np.sort(eigvals)[::-1]
eigvals = 100 * eigvals / np.sum(eigvals) # Convert to percent change
eigvals
# Project the EEG data onto the chosen principal component for each trial
= np.zeros((EEG['pnts'][0][0], EEG['trials'][0][0]))
pcadata for i in range(EEG['trials'][0][0]):
# Calculate the PCA scores (projections) for the chosen component
= np.dot(pc[:, pcanum-1].T, EEG['data'][:, :, i] - np.mean(EEG['data'][:, :, i], axis=1, keepdims=True))
pcadata[:, i]
= 2
min_freq = 80
max_freq = 30
num_frex
# Define wavelet parameters
= np.arange(-1, 1 + 1/EEG['srate'][0][0], 1/EEG['srate'][0][0])
time = np.logspace(np.log10(min_freq), np.log10(max_freq), num_frex)
frex = np.logspace(np.log10(3), np.log10(10), num_frex) / (2 * np.pi * frex)
s
# Define convolution parameters
= len(time)
n_wavelet = EEG['pnts'][0][0] * EEG['trials'][0][0]
n_data = n_wavelet + n_data - 1
n_convolution = int(2**np.ceil(np.log2(n_convolution)))
n_conv_pow2 = (n_wavelet - 1) // 2
half_of_wavelet_size
# Get FFT of data
= fft(pcadata.flatten('F'), n_conv_pow2)
eegfft
# Initialize
= np.zeros((num_frex, EEG['pnts'][0][0])) # frequencies X time X trials
eegpower
= [np.argmin(np.abs(EEG['times'][0] - t)) for t in [-500, -200]]
baseidx
# Loop through frequencies and compute synchronization
for fi in range(num_frex):
= fft(np.exp(2 * 1j * np.pi * frex[fi] * time) * np.exp(-time ** 2 / (2 * (s[fi] ** 2))), n_conv_pow2)
wavelet
= ifft(wavelet * eegfft)
eegconv = eegconv[:n_convolution]
eegconv = eegconv[half_of_wavelet_size:-half_of_wavelet_size]
eegconv
= np.mean(np.abs(np.reshape(eegconv, (EEG['pnts'][0][0], EEG['trials'][0][0]), 'F')) ** 2, axis=1)
temppower = 10 * np.log10(temppower / np.mean(temppower[baseidx[0]:baseidx[1]+1]))
eegpower[fi, :]
# create channel montage
= [chan['labels'][0] for chan in EEG['chanlocs'][0]]
chan_labels = np.vstack([-1*EEG['chanlocs']['Y'], EEG['chanlocs']['X'], EEG['chanlocs']['Z']]).T
coords # remove channels outside of head
= np.where(np.array([chan['radius'][0][0] for chan in EEG['chanlocs'][0]]) > 0.54)
exclude_chans = np.delete(coords, exclude_chans, axis=0)
coords = np.delete(chan_labels, exclude_chans)
chan_labels = np.delete(pc, exclude_chans, axis=0)
pc_lp = make_dig_montage(ch_pos=dict(zip(chan_labels, coords)), coord_frame='head')
montage
# create MNE Info and Evoked object
= create_info(list(chan_labels), EEG['srate'], ch_types='eeg')
info
# Plotting the results
= plt.subplot(221)
ax = EvokedArray(pc_lp[:, pcanum-1, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', axes=ax, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere222)
plt.subplot('times'][0], np.mean(pcadata, axis=1))
plt.plot(EEG[-200, 1200])
plt.xlim([
212)
plt.subplot('times'][0], frex, eegpower, 40, cmap='jet')
plt.contourf(EEG[-3, 3])
plt.clim([-200, 1000])
plt.xlim(['log')
plt.yscale(6))
plt.yticks(np.logspace(np.log10(min_freq), np.log10(max_freq), f'{f:.1f}' for f in np.logspace(np.log10(min_freq), np.log10(max_freq), 6)])
plt.gca().set_yticklabels([f'TF power from component {pcanum}')
plt.title(
plt.tight_layout() plt.show()
Figure 23.5
# Recompute PCA
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(EEG['trials'][0][0]):
= EEG['data'][:, :, i] - np.mean(EEG['data'][:, :, i], axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= EEG['trials'][0][0]
covar = np.linalg.eig(covar)
eigvals, pc = 100 * np.sort(eigvals)[::-1] / np.sum(eigvals) # Convert to percent change
eigvals
# Plot eigenvalues as percent variance accounted for
1, len(eigvals)+1), eigvals, '-o', markerfacecolor='w')
plt.plot(np.arange(-1, 70])
plt.ylim([0, 65])
plt.xlim([
# Amount of variance expected by chance, computed analytically
1, EEG['nbchan'][0][0]], [100 / EEG['nbchan'][0][0]] * 2, 'k')
plt.plot([
# Amount of variance expected by chance, computed based on random data
= 1000
nperms = np.zeros((nperms, EEG['nbchan'][0][0]))
permeigvals for permi in range(nperms):
# Randomize ERP time points within each channel
= np.copy(erp)
randdat for ch in range(erp.shape[0]):
np.random.shuffle(randdat[ch, :])# Compute covariance matrix of the randomized data
= np.dot(randdat, randdat.T) / (EEG['pnts'][0][0] - 1)
covar # Perform eigenvalue decomposition
= np.linalg.eig(covar)
eigvals, pc = np.sort(eigvals)[::-1] # Sort eigenvalues in descending order
eigvals = 100 * eigvals / np.sum(eigvals) # Convert to percent change
permeigvals[permi, :]
1, len(np.mean(permeigvals, axis=0))+1), np.mean(permeigvals, axis=0), 'r-o', markerfacecolor='w')
plt.plot(np.arange('% var. accounted for', 'chance-level (alg)', 'chance-level (perm.test)'])
plt.legend([ plt.show()
Figure 23.6
# Define parameters
= 1 # 1 for panel A; 2 for panel B
whichcomp
= np.arange(-200, 1201, 50)
centertimes = 200 # ms on either side of center times
timewindow = np.array([-100, 200, 500, 1000] if whichcomp == 1 else [0, 300, 750, 1000])
maptimes = (80000, 150000) if whichcomp == 1 else (-200000, 200000)
clim
# Initialize variables
= np.zeros(len(centertimes))
pcvariance = np.zeros((len(centertimes), EEG['nbchan'][0][0]))
firstpcas
= [np.argmin(np.abs(EEG['times'][0] - t)) for t in centertimes]
timesidx = round(timewindow / (1000 / EEG['srate'][0][0]))
timewinidx = [np.argmin(np.abs(centertimes - t)) for t in maptimes]
mapsidx
# Perform temporally localized PCA
for ti, center_time in enumerate(centertimes):
# Temporally localized covariance
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(EEG['trials'][0][0]):
= EEG['data'][:, timesidx[ti] - timewinidx:timesidx[ti] + timewinidx + 1, i]
eeg = eeg - np.mean(eeg, axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= EEG['trials'][0][0]
covar
# PCA via eigenvalue decomposition
= np.linalg.eig(covar)
eigvals, pc = pc[:, np.argsort(-eigvals)]
pc = np.sort(eigvals)[::-1]
eigvals = 100 * eigvals / np.sum(eigvals) # Convert to percent change
eigvals
= eigvals[whichcomp - 1]
pcvariance[ti] = pc[:, whichcomp - 1]
firstpcas[ti, :]
# Adjust the sign of the principal components for consistent visualization.
# Just do it for the first component since its visualization is only in the positive range.
if whichcomp == 1:
for i in range(firstpcas.shape[0]):
if np.sum(firstpcas[i, :]) < 0:
= -firstpcas[i, :]
firstpcas[i, :]
# Plot variance over time
plt.plot(centertimes, pcvariance)-200, 1200])
plt.xlim(['Time (ms)')
plt.xlabel(f'% variance from PC{whichcomp}')
plt.ylabel(
plt.show()
# create channel montage
= [chan['labels'][0] for chan in EEG['chanlocs'][0]]
chan_labels = np.vstack([-1*EEG['chanlocs']['Y'], EEG['chanlocs']['X'], EEG['chanlocs']['Z']]).T
coords # remove channels outside of head
= np.where(np.array([chan['radius'][0][0] for chan in EEG['chanlocs'][0]]) > 0.54)
exclude_chans = np.delete(coords, exclude_chans, axis=0)
coords = np.delete(chan_labels, exclude_chans)
chan_labels = np.delete(firstpcas, exclude_chans, axis=1)
firstpcas = make_dig_montage(ch_pos=dict(zip(chan_labels, coords)), coord_frame='head')
montage
# create MNE Info and Evoked object
= create_info(list(chan_labels), EEG['srate'], ch_types='eeg')
info
# Plot topomaps at specified times
for i, maptime in enumerate(maptimes):
= plt.subplot(int(np.ceil(len(maptimes) / np.ceil(np.sqrt(len(maptimes))))), int(np.ceil(np.sqrt(len(maptimes)))), i + 1)
ax = EvokedArray(firstpcas[mapsidx[i], :, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', vlim=clim, axes=ax, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(spheref'PC{whichcomp} from {maptime} ms')
plt.title(
plt.tight_layout() plt.show()
Figure 23.7
# Filter parameters
= 12 # in Hz
center_freq = 3 # Hz +/- the center frequency
filter_frequency_spread = 0.2
transition_width
# Construct filter kernel
= EEG['srate'][0][0] / 2
nyquist = round(3 * (EEG['srate'][0][0] / (center_freq - filter_frequency_spread)))
filter_order
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread), (center_freq - filter_frequency_spread), (center_freq + filter_frequency_spread), (1 + transition_width) * (center_freq + filter_frequency_spread), nyquist] / nyquist
ffrequencies = [0, 0, 1, 1, 0, 0]
idealresponse = firls(filter_order, ffrequencies, idealresponse)
filterweights
# Filter the data
= filtfilt(filterweights, 1, np.reshape(EEG['data'], (EEG['nbchan'][0][0], EEG['pnts'][0][0] * EEG['trials'][0][0]), 'F'))
filter_result = np.reshape(filter_result, (EEG['nbchan'][0][0], EEG['pnts'][0][0], EEG['trials'][0][0]), 'F')
filter_result
# Perform PCA on filtered data
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(EEG['trials'][0][0]):
= filter_result[:, :, i] - np.mean(filter_result[:, :, i], axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= EEG['trials'][0][0]
covar
= np.linalg.eig(covar)
eigvals, pc
= pc[:, np.argsort(-eigvals)]
pc = np.sort(eigvals)[::-1]
eigvals = 100 * eigvals / np.sum(eigvals) # Convert to percent change
eigvals
# create channel montage
= [chan['labels'][0] for chan in EEG['chanlocs'][0]]
chan_labels = np.vstack([-1*EEG['chanlocs']['Y'], EEG['chanlocs']['X'], EEG['chanlocs']['Z']]).T
coords # remove channels outside of head
= np.where(np.array([chan['radius'][0][0] for chan in EEG['chanlocs'][0]]) > 0.54)
exclude_chans = np.delete(coords, exclude_chans, axis=0)
coords = np.delete(chan_labels, exclude_chans)
chan_labels = np.delete(pc, exclude_chans, axis=0)
pc_lp = make_dig_montage(ch_pos=dict(zip(chan_labels, coords)), coord_frame='head')
montage
# create MNE Info and Evoked object
= create_info(list(chan_labels), EEG['srate'], ch_types='eeg')
info
# Plot the first 9 principal components
for i in range(9):
10, figsize=(10, 10))
plt.figure(= plt.subplot(3, 3, i + 1)
ax = EvokedArray(pc_lp[:, i, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', axes=ax, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(spheref'PC #{i + 1}, eigval={eigvals[i]:.4f}')
plt.title(
11, figsize=(10, 10))
plt.figure(9, 1, i + 1)
plt.subplot('times'][0], pc[:, i].T @ np.mean(filter_result, axis=2))
plt.plot(EEG[0, color='k')
plt.axhline(-200, 1200])
plt.xlim([f'PC #{i + 1}, eigval={eigvals[i]:.4f}')
plt.title(
plt.tight_layout() plt.show()
Figure 23.8
Note about this code: The legend of this figure (page 304) states that the PCA computed on all trials and then the weights were separately applied to the first and last 30 trials. However, in the code below (and thus, in the book figure), the PCA is actually computed separately on the first and last 30 trials.
# PCA on first 30 trials
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(30):
= filter_result[:, :, i] - np.mean(filter_result[:, :, i], axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= 30
covar
= np.linalg.eig(covar)
eigvals, pc_first = pc_first[:, np.argsort(-eigvals)]
pc_first
# PCA on last 30 trials
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
covar for i in range(EEG['trials'][0][0] - 30, EEG['trials'][0][0]):
= filter_result[:, :, i] - np.mean(filter_result[:, :, i], axis=1, keepdims=True)
eeg += np.dot(eeg, eeg.T) / (EEG['pnts'][0][0] - 1)
covar /= 30
covar = np.linalg.eig(covar)
eigvals, pc_last = pc_last[:, np.argsort(-eigvals)]
pc_last
# Plot the first 9 principal components for first and last 30 trials
for i in range(9):
3, 3, i + 1)
plt.subplot('times'][0], pc_first[:, i].T @ np.mean(filter_result[:, :, :30], axis=2))
plt.plot(EEG['times'][0], pc_last[:, i].T @ np.mean(filter_result[:, :, -30:], axis=2), 'r')
plt.plot(EEG[-200, 1200])
plt.xlim([f'PC #{i + 1}')
plt.title('first 30 trials', 'last 30 trials'])
plt.legend([ plt.show()