import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
from scipy.fft import fft, ifft
from scipy.stats import norm, pearsonr
from mne import create_info, EvokedArray
from mne.channels import make_dig_montage
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import dijkstra
Chapter 31
Chapter 31
Analyzing Neural Time Series Data
Python code for Chapter 31 – 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 31.2
# Load data
= loadmat('../data/sampleEEGdata.mat')['EEG'][0, 0]
EEG
# Specify some time-frequency parameters
= 10 # Hz
center_freq = 200 # in ms
time2analyze
# Wavelet and FFT parameters
= np.arange(-1, 1 + 1/EEG['srate'][0, 0], 1/EEG['srate'][0, 0])
time = (len(time) - 1) // 2
half_wavelet = len(time)
n_wavelet = EEG['pnts'][0, 0] * EEG['trials'][0, 0]
n_data = n_wavelet + n_data - 1
n_convolution
# Initialize connectivity output matrix
= np.zeros((EEG['nbchan'][0, 0], EEG['nbchan'][0, 0]))
connectivitymat
# Time in indices
= np.argmin(np.abs(EEG['times'][0] - time2analyze))
tidx
# Create wavelet and take FFT
= 5 / (2 * np.pi * center_freq)
s = fft(np.exp(2 * 1j * np.pi * center_freq * time) * np.exp(-time**2 / (2 * (s**2))), n_convolution)
wavelet_fft
# Compute analytic signal for all channels
= np.zeros((EEG['nbchan'][0, 0], EEG['pnts'][0, 0], EEG['trials'][0, 0]), dtype=complex)
analyticsignals for chani in range(EEG['nbchan'][0, 0]):
# FFT of data
= fft(EEG['data'][chani, :, :].flatten('F'), n_convolution)
data_fft
# Convolution
= ifft(wavelet_fft * data_fft, n_convolution)
convolution_result = convolution_result[half_wavelet:-half_wavelet]
convolution_result
= np.reshape(convolution_result, (EEG['pnts'][0, 0], EEG['trials'][0, 0]), 'F')
analyticsignals[chani]
# Now compute all-to-all connectivity
for chani in range(EEG['nbchan'][0, 0]):
for chanj in range(chani, EEG['nbchan'][0, 0]): # Note that you don't need to start at 1
= analyticsignals[chani, tidx] * np.conj(analyticsignals[chanj, tidx])
xsd
# Connectivity matrix (phase-lag index on upper triangle; ISPC on lower triangle)
= np.abs(np.mean(np.sign(np.imag(xsd))))
connectivitymat[chani, chanj] = np.abs(np.mean(np.exp(1j * np.angle(xsd))))
connectivitymat[chanj, chani]
# Plotting the connectivity matrix
plt.figure()='equal', vmin=0, vmax=0.7)
plt.imshow(connectivitymat, aspect= np.arange(1, EEG['nbchan'][0, 0] + 1, 8)
xticks = np.arange(1, EEG['nbchan'][0, 0] + 1, 8)
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[
plt.colorbar() plt.show()
Figure 31.4
=(10, 10))
plt.figure(figsize
# Subplot 221
221)
plt.subplot(# Get upper part of matrix
= connectivitymat[np.triu_indices_from(connectivitymat, k=1)]
temp # Threshold is one std above median connectivity value
= np.std(temp) + np.median(temp)
pli_thresh # Plot histogram and vertical line at threshold
=30, alpha=0.75)
plt.hist(temp, bins='magenta', linewidth=2)
plt.axvline(pli_thresh, color'Phase-lag index')
plt.xlabel('Count')
plt.ylabel(0, 1])
plt.xlim([
# Subplot 222
222)
plt.subplot(# Get lower part of matrix
= connectivitymat[np.tril_indices_from(connectivitymat, k=-1)]
temp # Find 1 std above median connectivity value
= np.std(temp) + np.median(temp)
ispc_thresh # Plot histogram and vertical line at threshold
=30, alpha=0.75)
plt.hist(temp, bins='magenta', linewidth=2)
plt.axvline(ispc_thresh, color'ISPC')
plt.xlabel('Count')
plt.ylabel(0, 1])
plt.xlim([
# Subplot 223
223)
plt.subplot(# Get the upper triangle of the matrix, excluding the diagonal
= np.triu(connectivitymat, k=1)
pli_mat # Find 1 std above median connectivity value
= pli_mat[pli_mat != 0] # Exclude zeros
temp # Apply the threshold and make the matrix symmetric
< pli_thresh] = 0
pli_mat[pli_mat = pli_mat + pli_mat.T # Add the transpose to include the lower triangle
pli_mat # Plot the matrix
='equal', vmin=0, vmax=0.7)
plt.imshow(pli_mat, aspect= np.arange(1, EEG['nbchan'][0, 0] + 1, 8)
xticks = np.arange(1, EEG['nbchan'][0, 0] + 1, 8)
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[
# Subplot 224
224)
plt.subplot(# Make symmetric phase-lag index connectivity matrix
= connectivitymat.copy()
ispc_mat # Eliminate upper triangle (including the diagonal)
=0)] = 0
ispc_mat[np.triu_indices_from(ispc_mat, k# Mirror lower triangle to upper triangle
= ispc_mat + ispc_mat.T
ispc_mat # Apply threshold
< ispc_thresh] = 0
ispc_mat[ispc_mat # Binarize connectivity matrix
= ispc_mat.astype(bool)
ispc_mat # Plot the matrix
='equal', vmin=0, vmax=0.7)
plt.imshow(ispc_mat, aspect= np.arange(1, EEG['nbchan'][0, 0] + 1, 8)
xticks = np.arange(1, EEG['nbchan'][0, 0] + 1, 8)
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[
plt.tight_layout() plt.show()
Figure 31.6
= np.sum(pli_mat > 0, axis=0)
degree_pli = np.sum(ispc_mat > 0, axis=0)
degree_ispc
# 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(degree_pli, exclude_chans)
degrees_pli = np.delete(degree_ispc, exclude_chans)
degrees_ispc = 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
=(10, 5))
plt.figure(figsize
# Subplot 121
= plt.subplot(121)
ax1 = EvokedArray(degrees_pli[:, 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, 25000000), axes=ax1, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere'Connectivity degree, phase-lag index')
plt.title(
# Subplot 122
= plt.subplot(122)
ax2 = EvokedArray(degrees_ispc[:, 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, 25000000), axes=ax2, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sphere'Connectivity degree, ISPC')
plt.title(
plt.tight_layout() plt.show()
Figure 31.7
(analyses are here; next cell plots results)
= np.logspace(np.log10(3), np.log10(40), 25)
frex = np.arange(-300, 1201, 50)
times2save = np.zeros(len(frex)) # Added threshold vector
thresh
# Wavelet and FFT parameters
= np.arange(-1, 1 + 1/EEG['srate'][0, 0], 1/EEG['srate'][0, 0])
time = (len(time) - 1) // 2
half_wavelet = 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 take FFT)
= np.zeros((len(frex), n_conv2), dtype=complex)
wavelets_fft = np.logspace(np.log10(4), np.log10(10), len(frex)) / (2 * np.pi * frex)
s for fi in range(len(frex)):
= fft(np.exp(2 * 1j * np.pi * frex[fi] * time) * np.exp(-time**2 / (2 * (s[fi]**2))), n_conv2)
wavelets_fft[fi, :]
# Find time indices
= [np.argmin(np.abs(EEG['times'][0] - t)) for t in times2save]
times2saveidx
# Initialize matrices
= np.zeros((EEG['nbchan'][0, 0], len(frex), len(times2save), EEG['trials'][0, 0]), dtype=complex)
alldata = np.zeros((EEG['nbchan'][0, 0], EEG['nbchan'][0, 0], len(frex), len(times2save)), dtype=complex)
tf_all2all = np.zeros((EEG['nbchan'][0, 0], len(frex), len(times2save)))
tf_degree
# First, run convolution for all electrodes and save results
for chani in range(EEG['nbchan'][0, 0]):
# FFT of activity at this electrode (note that this is done outside the frequency loop)
= fft(EEG['data'][chani].flatten('F'), n_conv2)
eeg_fft
# Loop over frequencies
for fi in range(len(frex)):
# Analytic signal from target
= ifft(wavelets_fft[fi, :] * eeg_fft, n_conv2)
conv_res = conv_res[:n_convolution]
conv_res = np.reshape(conv_res[half_wavelet:-half_wavelet], (EEG['pnts'][0, 0], EEG['trials'][0, 0]), 'F')
asig
# Store the required time points
= asig[times2saveidx, :]
alldata[chani, fi, :, :]
# Now that we have all the data, compute all-to-all connectivity
for chani in range(EEG['nbchan'][0, 0]):
for chanj in range(chani + 1, EEG['nbchan'][0, 0]):
# Compute connectivity
= alldata[chani] * np.conj(alldata[chanj])
xsd
# Connectivity matrix (phase-lag index or ISPC; comment one or the other line)
# tf_all2all[chani, chanj] = np.abs(np.mean(np.sign(np.imag(xsd)), axis=2)) # PLI
= np.abs(np.mean(np.exp(1j * np.angle(xsd)), axis=2)) # ISPC
tf_all2all[chani, chanj]
# Now that we have a one-to-all connectivity, threshold the connectivity matrix
# (separate threshold for each frequency)
for fi in range(len(frex)):
# Use the absolute values (magnitudes) of the complex numbers for thresholding
= np.abs(tf_all2all[:, :, fi, :][np.triu_indices(EEG['nbchan'][0, 0], k=1)])
tempsynch = np.median(tempsynch) + np.std(tempsynch)
thresh[fi]
# Isolate, threshold, binarize
for ti in range(tf_all2all.shape[3]):
= np.abs(tf_all2all[:, :, fi, ti]) # Use the magnitude for thresholding
temp = temp + temp.T # Make symmetric matrix
temp = temp > thresh[fi] # Threshold and binarize
temp = np.sum(temp, axis=0) # Compute degree tf_degree[:, fi, ti]
Figure 31.7 (plotting)
# Show topographical maps
= [5, 9] # Hz
freqs2plot = [100, 200, 300] # ms
times2plot = (0, 20000000)
clim
# 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(tf_degree, exclude_chans, axis=0)
tf_degrees = 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
=(12, 8))
plt.figure(figsizefor fi, freq in enumerate(freqs2plot):
for ti, time in enumerate(times2plot):
= ti + 1 + fi * len(times2plot)
plt_idx = plt.subplot(len(freqs2plot), len(times2plot), plt_idx)
ax = np.argmin(np.abs(frex - freq))
freq_idx = np.argmin(np.abs(times2save - time))
time_idx = EvokedArray(tf_degrees[:, freq_idx, time_idx, 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=clim, axes=ax, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensorsf"{time} ms, {freq} Hz")
plt.title(
plt.tight_layout() plt.show()
Figure 31.8
= 'FCz' # Change to 'oz' or any other electrode as needed
electrode2plot = [-300, -100]
baselineperiod = [-10, 10]
clim
# Convert baseline period time to indices
= [np.argmin(np.abs(times2save - t)) for t in baselineperiod]
baseidx # Subtract baseline
= tf_degree - np.mean(tf_degree[:, :, baseidx[0]:baseidx[1]+1], axis=2, keepdims=True)
tf_degree_base
=(8, 6))
plt.figure(figsize# Contourf plot for time-frequency connectivity degree at the specified electrode
# Note that we are now using the 2D slice for the contour plot
'chanlocs'][0]['labels']==electrode2plot, :, :]), 20, cmap='viridis')
plt.contourf(times2save, frex, np.squeeze(tf_degree_base[EEG[
plt.clim(clim)'log')
plt.yscale(round(np.logspace(np.log10(frex[0]), np.log10(frex[-1]), 6)))
plt.yticks(np.round(np.logspace(np.log10(frex[0]), np.log10(frex[-1]), 6)))
plt.gca().set_yticklabels(np.'Time (ms)')
plt.xlabel('Frequency (Hz)')
plt.ylabel(f'TF connectivity degree at electrode {electrode2plot}')
plt.title(
plt.show()
Figure 31.10
= [5, 9] # Hz
freqs2plot = [100, 200, 300] # ms
times2plot = [400000, 800000]
clim
# 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 = 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
# Open two figures ('h' for handle)
= plt.figure(figsize=(12, 8))
figh1 = plt.figure(figsize=(12, 8))
figh2
for fi, freq in enumerate(freqs2plot):
for ti, time in enumerate(times2plot):
# Frequency index
= np.argmin(np.abs(frex - freq))
fidx
# Extract thresholded connectivity matrix
= tf_all2all[:, :, fidx, np.argmin(np.abs(times2save - time))]
connmat = connmat + connmat.T # Make symmetric matrix
connmat = connmat > thresh[fidx] # Threshold and binarize
connmat
# Initialize
= np.zeros(EEG['nbchan'][0, 0])
clustcoef
for chani in range(EEG['nbchan'][0, 0]):
# Find neighbors (suprathreshold connections)
= np.where(connmat[chani, :] > 0)[0]
neighbors = len(neighbors)
n
# Cluster coefficient not computed for islands
if n > 1:
# "Local" network of neighbors
= connmat[np.ix_(neighbors, neighbors)]
localnetwork # Localnetwork is symmetric; remove redundant values by replacing with NaN
= localnetwork + np.tril(np.nan * np.ones(localnetwork.shape))
localnetwork # Compute cluster coefficient (neighbor connectivity scaled)
= 2 * np.nansum(localnetwork) / (n * (n - 1))
clustcoef[chani]
# Topoplots
plt.figure(figh1.number)= plt.subplot(len(freqs2plot), len(times2plot), ti + 1 + fi * len(times2plot))
ax1 = np.delete(clustcoef, exclude_chans)
clustcoefs = EvokedArray(clustcoefs[:, 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=clim, axes=ax1, show=False, times=-1, time_format='', colorbar=False)
evoked.plot_topomap(sensorsf'Cluster coefficient: {time} ms, {freq} Hz')
plt.title(
# Relationship between degree and cluster coefficient
plt.figure(figh2.number)len(freqs2plot), len(times2plot), ti + 1 + fi * len(times2plot))
plt.subplot(= np.sum(connmat > 0, axis=0)
degree
plt.scatter(degree, clustcoef)= pearsonr(degree, clustcoef)
r, p -0.05, 1.05])
plt.ylim([0, 30])
plt.xlim(['Degree')
plt.xlabel('Cluster coefficient')
plt.ylabel(f'Correlation: r={r:.2f}, p={p:.2f}')
plt.title(
plt.tight_layout() plt.show()
Figure 31.11
= np.linspace(0, 3, 20)
thresholds = np.zeros((EEG['nbchan'][0, 0], len(thresholds)))
clustercoefficients
for ti, thresh_val in enumerate(thresholds):
# Find frequency index
= np.argmin(np.abs(frex - freqs2plot[0]))
fidx
# Extract thresholded connectivity matrix
= tf_all2all[:, :, fidx, np.argmin(np.abs(times2save - times2plot[2]))]
connmat = tf_all2all[:, :, fidx, :][tf_all2all[:, :, fidx, :] > 0]
tmpsynch = np.median(tmpsynch) + thresh_val * np.std(tmpsynch)
tthresh = connmat + connmat.T # Make symmetric matrix
connmat = connmat > tthresh # Threshold and binarize
connmat
for chani in range(EEG['nbchan'][0, 0]):
# Find neighbors (suprathreshold connections)
= np.where(connmat[chani, :] > 0)[0]
neighbors = len(neighbors)
n
# Cluster coefficient not computed for islands
if n > 1:
# "Local" network of neighbors
= connmat[np.ix_(neighbors, neighbors)].astype(float)
localnetwork # Localnetwork is symmetric; remove redundant values by replacing with NaN
+= np.tril(np.nan * np.ones(localnetwork.shape))
localnetwork # Compute cluster coefficient (neighbor connectivity scaled)
= 2 * np.nansum(localnetwork) / (n * (n - 1))
clustercoefficients[chani, ti]
=(10, 8))
plt.figure(figsize=0.5)
plt.plot(thresholds, clustercoefficients.T, alpha=0), 'k', linewidth=3)
plt.plot(thresholds, np.mean(clustercoefficients, axis'Threshold (number of standard deviations above median)')
plt.xlabel('Clustering coefficient')
plt.ylabel(0, 3])
plt.xlim([-0.025, 1.025])
plt.ylim([ plt.show()
Figures 31.13/14
(this cell takes a long time to run…)
= False # if False, simulate a network (True/False for Figures 13/14)
use_real_network
= 10
freq2use = 100
time2use
# Frequency index
= np.argmin(np.abs(frex - freq2use))
fidx
= tf_all2all[:, :, fidx, np.argmin(np.abs(times2save - time2use))]
connmat = connmat + connmat.T # Make symmetric matrix
connmat = connmat > thresh[fidx] # Threshold and binarize
connmat
if not use_real_network:
= 1000 # number of nodes
n = 10 # neighbor connectivity
k = np.zeros((n, n))
connmat for i in range(n):
# set neighbors to 1 (special cases for start and end of network)
max(1, i - k // 2):min(n, i + k // 2)] = 1
connmat[i,
# probabilities of rewiring
= np.logspace(np.log10(0.0001), np.log10(1), 20)
probs
= np.zeros(len(probs))
cp = np.zeros(len(probs))
lp
# loop over a few networks to make plots look a bit nicer
for neti in range(10):
for probi, prob in enumerate(probs):
# rewire
# find which edges to rewire
= np.where(np.tril(connmat) != 0)
real_edges = np.random.choice(len(real_edges[0]), size=int(round(prob * len(real_edges[0]))), replace=False)
edges_to_rewire
# rewired connectivity matrix
= connmat.copy()
connmat_rewired
# loop through edges and change target
for edge_idx in edges_to_rewire:
# find XY coordinates
= real_edges[0][edge_idx], real_edges[1][edge_idx]
x, y
# find possible edges to change (cannot already be an edge)
= np.where(connmat_rewired[x, :] == 0)[0]
edges2change
# rewire
= np.random.choice(edges2change, 1)
y2rewire = 1
connmat_rewired[x, y2rewire] = 1
connmat_rewired[y2rewire, x]
# set original to zero
= 0
connmat_rewired[x, y] = 0
connmat_rewired[y, x]
# mirror matrix
= np.tril(connmat_rewired) + np.tril(connmat_rewired).T
connmat_rewired # binarize
= (connmat_rewired > 0).astype(int)
connmat_rewired
# compute cluster coefficient and path lengths
= np.zeros(EEG['nbchan'][0, 0])
clustcoef_rewired
for chani in range(EEG['nbchan'][0, 0]):
# Find neighbors (suprathreshold connections)
= np.where(connmat_rewired[chani, :] > 0)[0]
neighbors = len(neighbors)
n
# Cluster coefficient not computed for islands
if n > 1:
# "Local" network of neighbors
= connmat_rewired[np.ix_(neighbors, neighbors)].astype(float)
localnetwork # Localnetwork is symmetric; remove redundant values by replacing with NaN
+= np.tril(np.nan * np.ones(localnetwork.shape))
localnetwork # Compute cluster coefficient (neighbor connectivity scaled)
= 2 * np.nansum(localnetwork) / (n * (n - 1))
clustcoef_rewired[chani]
+= np.nanmean(clustcoef_rewired)
cp[probi] = dijkstra(csgraph=csr_matrix(connmat_rewired), directed=False, unweighted=True)
temppathlengths += np.mean(temppathlengths[temppathlengths != np.inf])
lp[probi]
# Save example networks from select probabilities
if probi == 0:
= connmat_rewired
network1 elif probi == 10:
= connmat_rewired
network10 elif probi == len(probs) - 1:
= connmat_rewired
networkend
/= 10
cp /= 10
lp
=(10, 8))
plt.figure(figsize/ cp[0], '-o', label='C_r/C')
plt.plot(probs, cp / lp[0], 'r-o', label='L_r/L')
plt.plot(probs, lp 'Probability of rewiring')
plt.xlabel(
plt.legend()'log')
plt.xscale(0], color='k', linestyle=':')
plt.axvline(probs[10], color='k', linestyle=':')
plt.axvline(probs[-1], color='k', linestyle=':')
plt.axvline(probs[0, 1.02])
plt.ylim([
plt.show()
# Determine the axis limits based on whether you are using a real network or a simulated network
if use_real_network:
= (1, EEG['nbchan'][0, 0])
xylims else:
= (1, 100)
xylims
# Create a figure and plot the three networks
=(15, 5))
plt.figure(figsize
# Subplot for network1
131)
plt.subplot(='gray', aspect='equal')
plt.imshow(network1, cmap
plt.xlim(xylims)
plt.ylim(xylims)
plt.gca().invert_yaxis()
# Subplot for network10
132)
plt.subplot(='gray', aspect='equal')
plt.imshow(network10, cmap
plt.xlim(xylims)
plt.ylim(xylims)
plt.gca().invert_yaxis()
# Subplot for networkend
133)
plt.subplot(='gray', aspect='equal')
plt.imshow(networkend, cmap
plt.xlim(xylims)
plt.ylim(xylims)
plt.gca().invert_yaxis()
plt.tight_layout() plt.show()
Figures 31.15/16
= 6
freq2use = 300
time2use = 1000
n_permutations
= np.linspace(0, 2, 20)
thresholds = np.zeros(len(thresholds))
swn_z
for threshi, thresh_val in enumerate(thresholds):
# Frequency index
= np.argmin(np.abs(frex - freq2use))
fidx
= tf_all2all[:, :, fidx, np.argmin(np.abs(times2save - time2use))]
connmat = tf_all2all[:, :, fidx, :][tf_all2all[:, :, fidx, :] > 0]
tmpsynch = np.median(tmpsynch) + thresh_val * np.std(tmpsynch)
tthresh = connmat + connmat.T # Make symmetric matrix
connmat = connmat > tthresh # Threshold and binarize
connmat = np.sum(connmat) // 2 # Number of connections (for random network)
nconnections
# find locations of lower triangle
= np.where(np.tril(connmat+1-np.eye(len(connmat))) != 0)
matrix_locs
= np.zeros(n_permutations)
swn_permutations
# compute real clustering coefficient and path lengths
= np.zeros(EEG['nbchan'][0, 0])
clustcoef_real for chani in range(EEG['nbchan'][0, 0]):
= np.where(connmat[chani, :] > 0)[0]
neighbors = len(neighbors)
n if n > 1:
= connmat[np.ix_(neighbors, neighbors)].astype(float)
localnetwork += np.tril(np.nan * np.ones(localnetwork.shape))
localnetwork = 2 * np.nansum(localnetwork) / (n * (n - 1))
clustcoef_real[chani] # average clustering coefficient over channels
= np.nanmean(clustcoef_real)
clustcoef_real
# average path length (remove zeros and infinities)
= dijkstra(csgraph=csr_matrix(connmat), directed=False, unweighted=True)
temppathlengths = np.mean(temppathlengths[temppathlengths != np.inf])
pathlengths_real
# first create 1000 random graphs and compute their clustering coefficients and path lengths
= np.zeros(n_permutations)
clustercoefficients_random = np.zeros(n_permutations)
pathlengths_random
for permi in range(n_permutations):
# generate random network
= np.zeros(connmat.shape)
connmat_random # find random locations in lower triangle
= np.random.choice(len(matrix_locs[0]), size=nconnections, replace=False)
edges2fill # set random locations to 1
0][edges2fill], matrix_locs[1][edges2fill]] = 1
connmat_random[matrix_locs[# mirror matrix
= np.tril(connmat_random) + np.tril(connmat_random).T
connmat_random # binarize
= (connmat_random > 0).astype(int)
connmat_random
# compute cluster coefficient and path lengths
= np.zeros(EEG['nbchan'][0, 0])
clustcoef_random for chani in range(EEG['nbchan'][0, 0]):
= np.where(connmat_random[chani, :] > 0)[0]
neighbors = len(neighbors)
n if n > 1:
= connmat_random[np.ix_(neighbors, neighbors)].astype(float)
localnetwork += np.tril(np.nan * np.ones(localnetwork.shape))
localnetwork = 2 * np.nansum(localnetwork) / (n * (n - 1))
clustcoef_random[chani] # average clustering coefficient over channels
= np.nanmean(clustcoef_random)
clustercoefficients_random[permi]
# average path length (remove zeros and infinities)
= dijkstra(csgraph=csr_matrix(connmat_random), directed=False, unweighted=True)
temppathlengths = np.mean(temppathlengths[temppathlengths != np.inf])
pathlengths_random[permi]
# compute small-world-networkness
for permi in range(n_permutations):
= np.random.choice(n_permutations, size=2, replace=False)
whichnetworks2use if pathlengths_random[whichnetworks2use[1]] != 0 and clustercoefficients_random[whichnetworks2use[1]] != 0:
= ((clustercoefficients_random[whichnetworks2use[0]] /
swn_permutations[permi] 1]]) /
clustercoefficients_random[whichnetworks2use[0]] /
(pathlengths_random[whichnetworks2use[1]]))
pathlengths_random[whichnetworks2use[else:
= np.nan
swn_permutations[permi]
# True swn and its z-value
= (clustcoef_real / np.mean(clustercoefficients_random)) / (pathlengths_real / np.mean(pathlengths_random))
swn_real = (swn_real - np.mean(swn_permutations)) / np.std(swn_permutations)
swn_z[threshi]
if threshi == len(thresholds) // 2:
=(10, 5))
plt.figure(figsize=50, alpha=0.5)
plt.hist(swn_permutations, bins='magenta', linewidth=3)
plt.axvline(swn_real, colorf'Small-world-networkness: Z={swn_z[threshi]:.2f}, p={1 - norm.cdf(np.abs(swn_z[threshi])):.4f}')
plt.title(
plt.show()
=(10, 5))
plt.figure(figsize'-o', markerfacecolor='white')
plt.plot(thresholds, swn_z, 'Threshold (Standard deviations above median)')
plt.xlabel('SWN_z')
plt.ylabel(-0.05, 2.05])
plt.xlim([ plt.show()