import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
from scipy.fftpack import fft, ifft
from mne import create_info, EvokedArray
from mne.channels import make_dig_montage
from laplacian_perrinX import laplacian_perrinX
from laplacian_nola import laplacian_nola
import time
Chapter 22
Chapter 22
Analyzing Neural Time Series Data
Python code for Chapter 22 – 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 22.2
# This figure was made by 'stepping-in' to the function laplacian_perrinX
# and then creating topographical maps of the Legendre polynomial.
Figure 22.3
# Load sample EEG dataset
= loadmat('../data/sampleEEGdata.mat')['EEG'][0, 0]
EEG
# Compute inter-electrode distances
= np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
interelectrodedist for chani in range(EEG['nbchan'][0][0]):
for chanj in range(chani + 1, EEG['nbchan'][0][0]):
= np.sqrt(
interelectrodedist[chani, chanj] 'chanlocs'][0][chani]['X'][0][0] - EEG['chanlocs'][0][chanj]['X'][0][0]) ** 2 +
(EEG['chanlocs'][0][chani]['Y'][0][0] - EEG['chanlocs'][0][chanj]['Y'][0][0]) ** 2 +
(EEG['chanlocs'][0][chani]['Z'][0][0] - EEG['chanlocs'][0][chanj]['Z'][0][0]) ** 2
(EEG[
)
= np.where(interelectrodedist > 0)
valid_gridpoints
# Extract XYZ coordinates from EEG structure
= np.array([chan['X'][0][0] for chan in EEG['chanlocs'][0]])
X = np.array([chan['Y'][0][0] for chan in EEG['chanlocs'][0]])
Y = np.array([chan['Z'][0][0] for chan in EEG['chanlocs'][0]])
Z
# Crreate the G and H matrices
= laplacian_perrinX(np.random.rand(len(X)), X, Y, Z, smoothing=1e-6)
surf_lap, G, H # Note we define the laplacian_perrinX function in the file laplacian_perrinX.py
# Plot G and H matrices
=(10, 8))
plt.figure(figsize221)
plt.subplot(='viridis')
plt.imshow(G, cmap'G')
plt.title(
222)
plt.subplot(='viridis')
plt.imshow(H, cmap'H')
plt.title(
223)
plt.subplot('r.')
plt.plot(interelectrodedist[valid_gridpoints], G[valid_gridpoints], 'm.')
plt.plot(interelectrodedist[valid_gridpoints], H[valid_gridpoints], 'G', 'H'])
plt.legend([0, 200])
plt.xlim([-0.065, 0.065])
plt.ylim(['Inter-electrode distances (mm)')
plt.xlabel('G or H')
plt.ylabel(
plt.tight_layout() plt.show()
Figure 22.4
In the book, figure 4 uses chan1 as Pz. The book also mentions that the surface Laplacian will attenuate the impact of EOG artifacts. This can be simulated here by setting chan1 to FPz.
= 'Pz'
chan1 = 'C4'
chan2 = 'C3'
chan3
= np.zeros(EEG['nbchan'][0][0])
eucdist1 = np.zeros(EEG['nbchan'][0][0])
eucdist2 = np.zeros(EEG['nbchan'][0][0])
eucdist3
= EEG['chanlocs'][0]['labels']==chan1
chan1idx = EEG['chanlocs'][0]['labels']==chan2
chan2idx = EEG['chanlocs'][0]['labels']==chan3
chan3idx
# Calculate the Euclidean distances
for chani in range(EEG['nbchan'][0][0]):
= np.sqrt((EEG['chanlocs'][0][chani]['X'][0][0] - EEG['chanlocs'][0][chan1idx]['X'][0][0])**2 +
eucdist1[chani] 'chanlocs'][0][chani]['Y'][0][0] - EEG['chanlocs'][0][chan1idx]['Y'][0][0])**2 +
(EEG['chanlocs'][0][chani]['Z'][0][0] - EEG['chanlocs'][0][chan1idx]['Z'][0][0])**2)
(EEG[= np.sqrt((EEG['chanlocs'][0][chani]['X'][0][0] - EEG['chanlocs'][0][chan2idx]['X'][0][0])**2 +
eucdist2[chani] 'chanlocs'][0][chani]['Y'][0][0] - EEG['chanlocs'][0][chan2idx]['Y'][0][0])**2 +
(EEG['chanlocs'][0][chani]['Z'][0][0] - EEG['chanlocs'][0][chan2idx]['Z'][0][0])**2)
(EEG[= np.sqrt((EEG['chanlocs'][0][chani]['X'][0][0] - EEG['chanlocs'][0][chan3idx]['X'][0][0])**2 +
eucdist3[chani] 'chanlocs'][0][chani]['Y'][0][0] - EEG['chanlocs'][0][chan3idx]['Y'][0][0])**2 +
(EEG['chanlocs'][0][chani]['Z'][0][0] - EEG['chanlocs'][0][chan3idx]['Z'][0][0])**2)
(EEG[
# Compute spatial frequencies
= 2 * np.exp(-eucdist1 ** 2 / (2 * 95 ** 2))
hi_spatfreq = np.exp(-eucdist2 ** 2 / (2 * 50 ** 2)) + np.exp(-eucdist3 ** 2 / (2 * 50 ** 2))
lo_spatfreq = laplacian_perrinX(hi_spatfreq + lo_spatfreq, X, Y, Z)
surf_lap_all, _, _
# 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(hi_spatfreq, exclude_chans)
hi_spatfreq = np.delete(lo_spatfreq, exclude_chans)
lo_spatfreq = np.delete(surf_lap_all, exclude_chans)
surf_lap_all = 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 surface Laplacian
= plt.subplots(2, 2, figsize=(10, 5))
fig, axs = EvokedArray(hi_spatfreq[:, 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, 0], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere0, 0].set_title('Low spatial frequency features')
axs[
= EvokedArray(lo_spatfreq[:, 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, 1], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere0, 1].set_title('High spatial frequency features')
axs[
= EvokedArray(hi_spatfreq[:, np.newaxis] + lo_spatfreq[:, 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, 0], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere1, 0].set_title('Low+high features')
axs[
= EvokedArray(surf_lap_all[:, 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, 1], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere1, 1].set_title('Laplacian of low+high features')
axs[
plt.tight_layout() plt.show()
Another example similar to Figure 4
# Define channels for analysis
= 'Cz'
chan1 = 'P5'
chan2
= np.zeros(EEG['nbchan'][0][0])
eucdist1 = np.zeros(EEG['nbchan'][0][0])
eucdist2
= EEG['chanlocs'][0]['labels']==chan1
chan1idx = EEG['chanlocs'][0]['labels']==chan2
chan2idx
# Calculate the Euclidean distances
for chani in range(EEG['nbchan'][0][0]):
= np.sqrt((EEG['chanlocs'][0][chani]['X'][0][0] - EEG['chanlocs'][0][chan1idx]['X'][0][0])**2 +
eucdist1[chani] 'chanlocs'][0][chani]['Y'][0][0] - EEG['chanlocs'][0][chan1idx]['Y'][0][0])**2 +
(EEG['chanlocs'][0][chani]['Z'][0][0] - EEG['chanlocs'][0][chan1idx]['Z'][0][0])**2)
(EEG[= np.sqrt((EEG['chanlocs'][0][chani]['X'][0][0] - EEG['chanlocs'][0][chan2idx]['X'][0][0])**2 +
eucdist2[chani] 'chanlocs'][0][chani]['Y'][0][0] - EEG['chanlocs'][0][chan2idx]['Y'][0][0])**2 +
(EEG['chanlocs'][0][chani]['Z'][0][0] - EEG['chanlocs'][0][chan2idx]['Z'][0][0])**2)
(EEG[
# Compute data to use
= np.exp(-eucdist1 ** 2 / (2 * 65 ** 2)) + np.exp(-eucdist2 ** 2 / (2 * 50 ** 2))
data2use
# Compute surface Laplacian
= laplacian_perrinX(data2use, X, Y, Z)
surf_lap, _, _
# 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(data2use, exclude_chans)
data2use = np.delete(surf_lap, exclude_chans)
surf_lap = 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 topographic maps using plot_topomap
= plt.subplots(1, 2, figsize=(10, 5))
fig, axs = EvokedArray(data2use[:, 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('Low spatial frequency features')
axs[
= EvokedArray(surf_lap[:, 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('High spatial frequency features')
axs[
plt.tight_layout() plt.show()
Figure 22.5
# Use the mean of the EEG data at a specific time point
= np.mean(EEG['data'][:, 320, :], axis=1)
data2use
# Compute surface Laplacian using Nunez and Perrin methods
= laplacian_nola(X, Y, Z, data2use, smoothing=100)
surf_lapN = laplacian_perrinX(data2use, X, Y, Z, smoothing=1e-5)
surf_lapP, _, _ # note: try changing the smoothing parameter above (last input argument) to
# see the effects of the smoothing (lambda) parameter. Reasonable values
# are 1e-4 to 1e-6, and the default parameter is 1e-5.
# 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(data2use, exclude_chans)
data2use = np.delete(surf_lapN, exclude_chans)
surf_lapN = np.delete(surf_lapP, exclude_chans)
surf_lapP = 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 topographic maps using plot_topomap
= plt.subplots(1, 3, figsize=(10, 5))
fig, axs = EvokedArray(data2use[:, 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=axs[0], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensors0].set_title('Raw data')
axs[
= EvokedArray(surf_lapN[:, 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=axs[1], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensors1].set_title('Laplacian (Nunez book)')
axs[
= EvokedArray(surf_lapP[:, 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=axs[2], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensors2].set_title('Laplacian (Perrin et al.)')
axs[
plt.tight_layout()
plt.show()
# Display spatial correlation between the two Laplacian methods
= np.corrcoef(surf_lapN, surf_lapP)[0, 1]
spatial_corr print(f"Spatial correlation: r={spatial_corr}")
Spatial correlation: r=0.9814967637990577
Figure 22.6
# Timing is included in case you want to test the Perrin and New Orleans methods
= []
timetest
# Start timing for the first laplacian function
= time.time()
start_time = laplacian_perrinX(EEG['data'], X, Y, Z)
lap_data, _, _ # Calculate elapsed time and store it
= time.time() - start_time
elapsed_time
timetest.append(elapsed_time)
# Start timing for the second laplacian function
= time.time()
start_time = laplacian_nola(X, Y, Z, EEG['data'])
lap_data2 # Calculate elapsed time and store it
= time.time() - start_time
elapsed_time
timetest.append(elapsed_time)
# Define times to plot
= np.arange(-100, 900, 100)
times2plot
# 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(EEG['data'], exclude_chans, axis=0)
eeg_data = np.delete(X, exclude_chans)
Xlp = np.delete(Y, exclude_chans)
Ylp = np.delete(Z, exclude_chans)
Zlp = 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 voltage and Laplacian maps at different time points
= plt.subplots(2, len(times2plot), figsize=(20, 10))
fig, ax for i, time_point in enumerate(times2plot):
# Find time index
= np.argmin(np.abs(EEG['times'] - time_point))
timeidx
# Get the mean data at the specified time point
= np.mean(eeg_data[:, timeidx, :], axis=1)
tempdata
# Plot voltage map (spatially unfiltered)
= EvokedArray(tempdata[:, 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', vlim=(-10000000, 10000000), axes=ax[0, i], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensors0, i].set_title(f'Voltage, {time_point} ms')
ax[
# Plot Laplacian map (spatially filtered)
= EvokedArray(laplacian_perrinX(tempdata, Xlp, Ylp, Zlp)[0][:, 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', vlim=(-40000000, 40000000), axes=ax[1, i], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensors1, i].set_title(f'Laplacian, {time_point} ms')
ax[
plt.tight_layout() plt.show()
Brief aside:
This figure shows that computing the Laplacian of the ERP is the same as computing the Laplacian of single trials and then taking the ERP. This is not surprising: the ERP is a linear transform of the single trials.
# 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(EEG['data'], exclude_chans, axis=0)
eeg_data = np.delete(X, exclude_chans)
X_lp = np.delete(Y, exclude_chans)
Y_lp = np.delete(Z, exclude_chans)
Z_lp = np.delete(lap_data, exclude_chans, axis=0)
lap_data_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 comparison
= plt.subplots(1, 2, figsize=(10, 5))
fig, axs = EvokedArray(laplacian_perrinX(np.mean(eeg_data[:, 320, :], axis=1), X_lp, Y_lp, Z_lp)[0][:, 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', vlim=(-40000000, 40000000), axes=axs[0], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensors
= EvokedArray(np.mean(lap_data_lp[:, 320, :], axis=1)[:, 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', vlim=(-40000000, 40000000), axes=axs[1], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensors
plt.tight_layout() plt.show()
Figure 22.7
# Define frequency and time of interest
= 8 # Hz
freq2use = 400 # ms
time2use
# FFT parameters
= np.arange(-1, 1 + 1/EEG['srate'][0][0], 1/EEG['srate'][0][0])
time = 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_conv2
# Create wavelet and get FFT
= fft(np.exp(2 * 1j * np.pi * freq2use * time) * np.exp(-time ** 2 / (2 * (4 / (2 * np.pi * freq2use)) ** 2)) / freq2use, n_conv2)
wavelet_fft = (len(time) - 1) // 2
half_of_wavelet_size
# Initialize
= np.zeros(EEG['data'].shape, dtype=complex)
allphases_pre = np.zeros(EEG['data'].shape, dtype=complex)
allphases_lap = np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
ispc_pre = np.zeros((EEG['nbchan'][0][0], EEG['nbchan'][0][0]))
ispc_lap = np.argmin(np.abs(EEG['times'] - time2use))
timeidx
# Get all phases
for chani in range(EEG['nbchan'][0][0]):
# First for nonspatially filtered data
= fft(EEG['data'][chani, :, :].flatten('F'), n_conv2)
fft_data = ifft(wavelet_fft * fft_data, n_conv2)
conv_res = conv_res[:n_convolution]
conv_res = conv_res[half_of_wavelet_size:-half_of_wavelet_size]
conv_res = np.reshape(conv_res, (EEG['pnts'][0][0], EEG['trials'][0][0]), 'F')
allphases_pre[chani, :, :]
# Then for Laplacian filtered data
= fft(lap_data[chani, :, :].flatten('F'), n_conv2)
fft_data = ifft(wavelet_fft * fft_data, n_conv2)
conv_res = conv_res[:n_convolution]
conv_res = conv_res[half_of_wavelet_size:-half_of_wavelet_size]
conv_res = np.reshape(conv_res, (EEG['pnts'][0][0], EEG['trials'][0][0]), 'F')
allphases_lap[chani, :, :]
# Compute synchrony
for chani in range(EEG['nbchan'][0][0]):
for chanj in range(chani + 1, EEG['nbchan'][0][0]):
# Cross-spectral density for nonspatially filtered data
= allphases_pre[chani, timeidx, :] * np.conj(allphases_pre[chanj, timeidx, :])
cd_pre = np.abs(np.mean(np.exp(1j * np.angle(cd_pre))))
ispc_pre[chani, chanj]
# Cross-spectral density for Laplacian filtered data
= allphases_lap[chani, timeidx, :] * np.conj(allphases_lap[chanj, timeidx, :])
cd_lap = np.abs(np.mean(np.exp(1j * np.angle(cd_lap))))
ispc_lap[chani, chanj]
# Mirror connectivity matrices
= ispc_pre + ispc_pre.T + np.eye(EEG['nbchan'][0][0])
ispc_pre = ispc_lap + ispc_lap.T + np.eye(EEG['nbchan'][0][0])
ispc_lap
# Plot ISPC as a function of electrode distances
=(10, 5))
plt.figure(figsize121)
plt.subplot('.')
plt.plot(interelectrodedist[valid_gridpoints], ispc_pre[valid_gridpoints], 0, 200])
plt.xlim([0, 1])
plt.ylim(['Electrode distances (mm)')
plt.xlabel('ISPC')
plt.ylabel(f'Spatially unfiltered ISPC at {freq2use} Hz')
plt.title(= np.corrcoef(interelectrodedist[valid_gridpoints], ispc_pre[valid_gridpoints])[0, 1]
r_pre f'R^2 = {r_pre ** 2:.4f}'])
plt.legend([
122)
plt.subplot('.')
plt.plot(interelectrodedist[valid_gridpoints], ispc_lap[valid_gridpoints], 0, 200])
plt.xlim([0, 1])
plt.ylim(['Electrode distances (mm)')
plt.xlabel('ISPC')
plt.ylabel(f'Laplacian filtered ISPC at {freq2use} Hz')
plt.title(= np.corrcoef(interelectrodedist[valid_gridpoints], ispc_lap[valid_gridpoints])[0, 1]
r_lap f'R^2 = {r_lap ** 2:.4f}'])
plt.legend([
plt.tight_layout() plt.show()
Figure 22.8
# 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(ispc_pre, exclude_chans, axis=1)
ispc_pre_lp = np.delete(ispc_lap, exclude_chans, axis=1)
ispc_lap_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 topographic maps of ISPC for a specific channel (e.g., channel 48)
= plt.subplots(1, 2, figsize=(10, 5))
fig, axs = EvokedArray(ispc_pre_lp[47, :, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', vlim=(0, 800000), axes=axs[0], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere0].set_title(f'ISPC_raw at {time2use} ms, {freq2use} Hz')
axs[
= EvokedArray(ispc_lap_lp[47, :, np.newaxis], info, tmin=EEG['xmin'][0][0])
evoked
evoked.set_montage(montage)=EEG['chanlocs'][0][0]['sph_radius'][0][0], cmap='jet', vlim=(0, 800000), axes=axs[1], show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere1].set_title(f'ISPC_lap at {time2use} ms, {freq2use} Hz')
axs[
plt.tight_layout() plt.show()