import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.io import loadmat
from scipy.fftpack import fft, ifft
# packages for interactive plots
import plotly.graph_objects as go
import plotly.io as pio
= "plotly_mimetype+notebook_connected"
pio.renderers.default from IPython.display import display, HTML
= '<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==" crossorigin="anonymous"></script>'
js display(HTML(js))
Chapter 13
Chapter 13
Analyzing Neural Time Series Data
Python code for Chapter 13 – 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 13.1
# Parameters
= 500 # Sampling rate in Hz
srate = 10 # Frequency of wavelet in Hz
f = np.arange(-1, 1 + 1/srate, 1/srate) # Time, from -1 to 1 second in steps of 1/sampling-rate
time = 6 / (2 * np.pi * f)
s
# Create a wavelet
= np.exp(2 * np.pi * 1j * f * time) * np.exp(-time**2 / (2 * s**2))
wavelet
# Plotting
= plt.figure(figsize=(10, 8))
fig
# Show the projection onto the real axis
= fig.add_subplot(221)
ax1 'm')
ax1.plot(time, np.real(wavelet), 'Time (ms)')
ax1.set_xlabel('real axis')
ax1.set_ylabel('Projection onto real and time axes')
ax1.set_title(
# Show the projection onto the imaginary axis
= fig.add_subplot(222)
ax2 'g')
ax2.plot(time, np.imag(wavelet), 'Time (ms)')
ax2.set_xlabel('imag axis')
ax2.set_ylabel('Projection onto imaginary and time axes')
ax2.set_title(
# Plot projection onto real and imaginary axes
= fig.add_subplot(223)
ax3 'k')
ax3.plot(np.real(wavelet), np.imag(wavelet), 'real axis')
ax3.set_xlabel('imag axis')
ax3.set_ylabel('Projection onto imaginary and time axes')
ax3.set_title(
# Plot real and imaginary projections simultaneously
= fig.add_subplot(224)
ax4 'b')
ax4.plot(time, np.real(wavelet), 'b:')
ax4.plot(time, np.imag(wavelet), -1, 1])
ax4.set_xlim([-1, 1])
ax4.set_ylim(['real part', 'imaginary part'])
ax4.legend([
plt.tight_layout() plt.show()
Figure 13.2
# Now show in 3D
= go.Figure(data=[go.Scatter3d(
fig =time,
x=np.real(wavelet),
y=np.imag(wavelet),
z='lines',
mode=dict(color='black')
line
)])
fig.update_layout(=dict(
scene='Time (ms)',
xaxis_title='Real Amplitude',
yaxis_title='Imag Amplitude',
zaxis_title=dict(
camera=dict(x=2, y=2, z=2)
eye
)
),=500,
width=500,
height='3-D Projection (click and spin with mouse)'
title
) fig.show()
Movie
# Parameters for the movie
= 6 # Frequency of the sine wave
frequency = 500 # Note: should be the same as the data
srate = np.arange(-0.5, 0.5 + 1/srate, 1/srate) # Vector of time
time
# Make wavelet
= np.exp(2 * 1j * np.pi * frequency * time) * np.exp(-time**2 / (2 * (4 / (2 * np.pi * frequency))**2))
wavelet
# Make a movie to compare cartesian and polar representation of wavelet
= plt.subplots(2, 1, figsize=(10, 6), facecolor=[0.6, 0.2, 0.7])
fig, axs
# Setup top row of data (real and imaginary in cartesian plot)
= axs[0].plot(time[0], np.real(wavelet[0]), 'b')
cplotR, = axs[0].plot(time[0], np.imag(wavelet[0]), 'b:')
cplotI, 0].set_ylim([-1, 1])
axs[0].set_xlim([time[0], time[-1]])
axs[0].set_xlabel('Time (s)')
axs[0].set_ylabel('Amplitude')
axs[0].set_title('Cartesian representation')
axs[
# Setup bottom row of data (polar representation)
= axs[1].plot(np.real(wavelet[0]), np.imag(wavelet[0]), 'k')
pplot, 1].set_ylim([-1, 1])
axs[1].set_xlim([-1, 1])
axs[1].set_title('Polar representation')
axs[1].set_xlabel('Real axis')
axs[1].set_ylabel('Imaginary axis')
axs[1].axis('square')
axs[
plt.tight_layout()
# Animation function
def update(ti):
+1], np.real(wavelet[:ti+1]))
cplotR.set_data(time[:ti+1], np.imag(wavelet[:ti+1]))
cplotI.set_data(time[:ti+1]), np.imag(wavelet[:ti+1]))
pplot.set_data(np.real(wavelet[:ti1].set_ylim([-1, 1])
axs[1].set_xlim([-1, 1])
axs[return cplotR, cplotI, pplot
# Create animation
= 5 # If you have a slow computer, set this to a higher number
timeskip = FuncAnimation(fig, update, frames=range(0, len(time), timeskip), blit=True)
ani
# Display the animation
HTML(ani.to_html5_video())
Figure 13.4
# Euler's formula: exp(1i*k) gives you a vector on a unit circle with angle k
= np.arange(-np.pi, np.pi, 0.25)
time
# Plotting
= plt.subplots(2, 2, figsize=(10, 8))
fig, axs
# cos(theta) + isin(theta)
0, 0].plot(np.cos(time), np.sin(time))
axs[0, 0].axis('square')
axs[0, 0].set_xlim([-1, 1])
axs[0, 0].set_ylim([-1, 1])
axs[0, 0].set_title('cos(theta) + isin(theta)')
axs[
# e^(i*theta)
0, 1].plot(np.exp(1j * time).real, np.exp(1j * time).imag)
axs[0, 1].axis('square')
axs[0, 1].set_xlim([-1, 1])
axs[0, 1].set_ylim([-1, 1])
axs[0, 1].set_title('e^(i*theta)')
axs[
# Both
1, 0].plot(np.cos(time), np.sin(time), 'bo-', markersize=8)
axs[1, 0].plot(np.exp(1j * time).real, np.exp(1j * time).imag, 'r.-')
axs[1, 0].axis('square')
axs[1, 0].set_xlim([-1, 1])
axs[1, 0].set_ylim([-1, 1])
axs[1, 0].set_title('Both')
axs[
# I need a better hobby
1, 1].plot(np.cos(time), np.sin(time))
axs[1, 1].plot(-.35 + np.cos(time) / 5, .35 + np.sin(time) / 5, 'm', linewidth=12) # Left eye
axs[1, 1].plot(.35 + np.cos(time) / 5, .35 + np.sin(time) / 5, 'r.', markersize=3) # Right eye
axs[= np.arange(-np.pi, 0, 0.5)
smile 1, 1].plot(np.cos(smile) / 3, -.35 + np.sin(smile) / 5, 'go', markersize=9) # Mouth
axs[1, 1].set_xlabel('Real axis')
axs[1, 1].set_ylabel('Imaginary axis')
axs[1, 1].axis('square')
axs[1, 1].set_xlim([-1, 1])
axs[1, 1].set_ylim([-1, 1])
axs[1, 1].set_title('I need a better hobby.')
axs[
plt.tight_layout() plt.show()
Figure 13.5
# Redefine time
= np.arange(-0.5, 0.5 + 1/srate, 1/srate) # Vector of time
time
# Plotting
=(10, 6))
plt.figure(figsize
# Plot real and imaginary parts of wavelet
=2)
plt.plot(time, np.real(wavelet), linewidth':', linewidth=2)
plt.plot(time, np.imag(wavelet),
# Plot cosine and sine
2 * np.pi * frequency * time), 'm', linewidth=2)
plt.plot(time, np.cos(2 * np.pi * frequency * time), 'm:', linewidth=2)
plt.plot(time, np.sin(
# Plot gaussian window
= np.exp(-time**2 / (2 * s**2))
gaus_win 'k')
plt.plot(time, gaus_win, -0.5, 0.5])
plt.xlim([-1.2, 1.2])
plt.ylim(['Time (s)')
plt.xlabel('real part of wavelet', 'imaginary part of wavelet', 'cosine', 'sine', 'Gaussian'])
plt.legend([ plt.show()
Figure 13.6
# Load sample EEG data
= loadmat('../data/sampleEEGdata.mat')['EEG'][0, 0]
EEG
# Create 10 Hz wavelet (kernel)
= np.arange(-(EEG['pnts'][0, 0]/EEG['srate'][0, 0]/2), EEG['pnts'][0, 0]/EEG['srate'][0, 0]/2, 1/EEG['srate'][0, 0])
time = 10 # Frequency of sine wave in Hz
f = 4 / (2 * np.pi * f)
s = np.exp(1j * 2 * np.pi * f * time) * np.exp(-time**2 / (2 * s**2))
wavelet
# Signal is one sine cycle
= np.arange(0, 1/f, 1/EEG['srate'][0, 0]) # One cycle is 1/f
timeS = np.sin(2 * np.pi * f * timeS)
signal
# Now zero-pad signal
= np.concatenate((np.zeros(int(EEG['pnts'][0, 0]/2 - len(timeS)/2)), signal, np.zeros(int(EEG['pnts'][0, 0]/2 - len(timeS)/2))))
signal
# Plotting
= plt.figure(figsize=(12, 8))
fig
# Plot waves
= fig.add_subplot(321)
ax1
ax1.plot(np.real(wavelet))200, len(time) - 200])
ax1.set_xlim([
= fig.add_subplot(323)
ax2
ax2.plot(signal)200, len(time) - 200])
ax2.set_xlim([
= fig.add_subplot(325)
ax3 'same')))
ax3.plot(np.real(np.convolve(wavelet, signal, 200, len(time) - 200])
ax3.set_xlim([-12, 12])
ax3.set_ylim([
# Now plot dot products at selected phase lags
= fig.add_subplot(322, projection='polar')
ax4 0, 12, 'k')
ax4.plot(= np.sum(wavelet[int(round(100/f))-3:] * signal[:-int(round(100/f))+3])
dp 0, np.abs(dp)], 'k')
ax4.plot([np.angle(dp), np.angle(dp)], [
= fig.add_subplot(324, projection='polar')
ax5 0, 12, 'k')
ax5.plot(= np.sum(wavelet[int(round(2.3*(100/f))-3):] * signal[:-int(round(2.3*(100/f))-3)])
dp 0, np.abs(dp)], 'k')
ax5.plot([np.angle(dp), np.angle(dp)], [
= fig.add_subplot(326, projection='polar')
ax6 0, 12, 'k')
ax6.plot(= np.sum(wavelet * signal)
dp 0, np.abs(dp)], 'k')
ax6.plot([np.angle(dp), np.angle(dp)], [
plt.tight_layout() plt.show()
Figure 13.8
# Create wavelet
= 6 # in Hz, as usual
frequency = np.arange(-1, 1 + 1/EEG['srate'][0, 0], 1/EEG['srate'][0, 0])
time = (4 / (2 * np.pi * frequency))**2 # Note that s is squared here rather than in the next line...
s = np.exp(2 * 1j * np.pi * frequency * time) * np.exp(-time**2 / (2 * s) / frequency)
wavelet
# FFT parameters
= len(wavelet)
n_wavelet = EEG['pnts'][0, 0]
n_data = n_wavelet + n_data - 1
n_convolution = (len(wavelet) - 1) // 2
half_of_wavelet_size
# FFT of wavelet and EEG data
= fft(wavelet, n_convolution)
fft_wavelet = fft(EEG['data'][46, :, 0], n_convolution) # FCz, trial 1
fft_data
= ifft(fft_wavelet * fft_data, n_convolution) * np.sqrt(s)
convolution_result_fft
# Cut off edges
= convolution_result_fft[half_of_wavelet_size:-half_of_wavelet_size]
convolution_result_fft
# Plot for comparison
= plt.subplots(3, 1, figsize=(10, 8))
fig, axs
0].plot(EEG['times'][0], np.real(convolution_result_fft))
axs[0].set_xlim([-1000, 1500])
axs[0].set_ylim([-50, 50])
axs[0].set_xlabel('Time (ms)')
axs[0].set_ylabel('Voltage (uV)')
axs[0].set_title(f'Projection onto real axis is filtered signal at {frequency} Hz.')
axs[
1].plot(EEG['times'][0], np.abs(convolution_result_fft)**2)
axs[1].set_xlim([-1000, 1500])
axs[1].set_ylim([0, 2000])
axs[1].set_xlabel('Time (ms)')
axs[1].set_ylabel('Power (uV^2)')
axs[1].set_title(f'Magnitude of projection vector squared is power at {frequency} Hz.')
axs[
2].plot(EEG['times'][0], np.angle(convolution_result_fft))
axs[2].set_xlim([-1000, 1500])
axs[2].set_ylim([-4, 4])
axs[2].set_xlabel('Time (ms)')
axs[2].set_ylabel('Phase angle (rad.)')
axs[2].set_title(f'Angle of vector is phase angle time series at {frequency} Hz.')
axs[
plt.tight_layout() plt.show()
Figure 13.9
# Real vs. Imaginary
= go.Figure(data=[go.Scatter3d(
fig =EEG['times'][0],
x=np.real(convolution_result_fft),
y=np.imag(convolution_result_fft),
z='lines',
mode=dict(color='blue')
line
)])
fig.update_layout(=dict(
scene='Time (ms)',
xaxis_title='Real Amplitude',
yaxis_title='Imag Amplitude',
zaxis_title=dict(
camera=dict(x=2, y=2, z=2)
eye
)
),=500,
width=500,
height='Click and drag to view real and imaginary amplitude'
title
)
fig.show()
# Amplitude vs. Phase
= go.Figure(data=[go.Scatter3d(
fig2 =EEG['times'][0],
x=np.abs(convolution_result_fft),
y=np.angle(convolution_result_fft),
z='lines',
mode=dict(color='blue')
line
)])
fig2.update_layout(=dict(
scene='Time (ms)',
xaxis_title='Amplitude',
yaxis_title='Phase angle (rad.)',
zaxis_title=dict(
camera=dict(x=2, y=2, z=2)
eye
)
),=500,
width=500,
height='Click and drag to view phase and amplitude'
title
)
fig2.show()
# Amplitude vs. Phase
= go.Figure(data=[go.Scatter3d(
fig3 =EEG['times'][0],
x=np.angle(convolution_result_fft),
y=np.real(convolution_result_fft),
z='lines',
mode=dict(color='red')
line
), go.Scatter3d(=EEG['times'][0],
x=np.angle(convolution_result_fft),
y=np.abs(convolution_result_fft),
z='lines',
mode=dict(color='blue')
line
)])
fig3.update_layout(=dict(
scene='Time (ms)',
xaxis_title='Phase angle (rad.)',
yaxis_title='Amplitude',
zaxis_title=dict(
camera=dict(x=2, y=2, z=2)
eye
)
),=500,
width=500,
height='Click and drag to view phase and amplitude'
title
) fig3.show()
Figure 13.10
= 500
srate = np.arange(-1,1 + 1/srate, 1/srate)
time
# Create a 9 Hz wavelet
= 9 # Frequency of wavelet in Hz
f = 6 / (2 * np.pi * f)
s = np.exp(2 * np.pi * 1j * f * time) * np.exp(-time**2 / (2 * s**2))
wavelet9
# Create a 10 Hz wavelet
= 10 # Frequency of wavelet in Hz
f = 6 / (2 * np.pi * f)
s = np.exp(2 * np.pi * 1j * f * time) * np.exp(-time**2 / (2 * s**2))
wavelet10
# Plotting
= plt.subplots(2, 1, figsize=(10, 6))
fig, axs
0].plot(time, np.real(wavelet9))
axs[0].plot(time, np.real(wavelet10), 'r')
axs[0].set_xlim([-1, 1])
axs[0].set_ylim([-1, 1])
axs[0].set_xlabel('Time (ms)')
axs[0].set_ylabel('Amplitude')
axs[
# Frequency domain representation
= np.linspace(0, srate/2, int(np.floor(len(time)/2)) + 1)
hz = fft(wavelet9)
fft9 = fft(wavelet10)
fft10
1].plot(hz, np.abs(fft9[:len(hz)]))
axs[1].plot(hz, np.abs(fft10[:len(hz)]), 'r')
axs[1].set_xlim([0, 25])
axs[1].set_ylim([0, 140])
axs[1].set_xlabel('Frequency (Hz)')
axs[1].set_ylabel('Amplitude')
axs[1].legend(['9 Hz wavelet', '10 Hz wavelet'])
axs[
plt.tight_layout() plt.show()
Figure 13.11
# Definitions, selections...
= 'FCz'
chan2use
= 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
# use the following lines to reproduce figure 13.14
# s = 3/(2*np.pi*frex)
# s = 10/(2*np.pi*frex)
# Define convolution parameters
= len(time)
n_wavelet = EEG['pnts'][0, 0] * EEG['trials'][0, 0]
n_data = n_wavelet + n_data - 1
n_convolution = 2**int(np.ceil(np.log2(n_convolution)))
n_conv_pow2 = (n_wavelet - 1) // 2
half_of_wavelet_size
# Find the index of the channel to use
= EEG["chanlocs"][0]["labels"] == chan2use
chanidx
# Reshape the data for the channel of interest
= np.reshape(EEG['data'][chanidx, :, :], (EEG['pnts'][0, 0] * EEG['trials'][0, 0]), order="F")
eegdata_chan
# Perform the FFT
= fft(eegdata_chan, n_conv_pow2)
eegfft
# Initialize
= np.zeros((num_frex, EEG['pnts'][0, 0])) # Frequencies X Time X Trials
eegpower
# Baseline indices
= np.array([np.argmin(np.abs(EEG['times'][0] - t)) for t in np.array([-500, -200])])
baseidx
# Loop through frequencies and compute synchronization
for fi in range(num_frex):
= fft(np.sqrt(1 / (s[fi] * np.sqrt(np.pi))) * np.exp(2 * 1j * np.pi * frex[fi] * time) * np.exp(-time**2 / (2 * (s[fi]**2))), n_conv_pow2)
wavelet
# Convolution
= ifft(wavelet * eegfft, n_conv_pow2)
eegconv = eegconv[:n_convolution]
eegconv = eegconv[half_of_wavelet_size:-half_of_wavelet_size]
eegconv
# Average power over trials (this code performs baseline transform, which you will learn about in chapter 18)
= np.mean(np.abs(np.reshape(eegconv, (EEG['pnts'][0, 0], EEG['trials'][0, 0]), order="F"))**2, axis=1)
temppower = 10 * np.log10(temppower / np.mean(temppower[baseidx[0]:baseidx[1]+1]))
eegpower[fi, :]
# Plotting
= plt.subplots(1, 2, figsize=(8, 6))
fig, axs
# Logarithmic frequency scaling
= axs[0].contourf(EEG['times'][0], frex, eegpower, 40, cmap='viridis', vmin=-3, vmax=3)
cf1 0].set_yscale('log')
axs[0].set_yticks(np.logspace(np.log10(min_freq), np.log10(max_freq), 6))
axs[0].set_yticklabels(np.round(np.logspace(np.log10(min_freq), np.log10(max_freq), 6), 1))
axs[0].set_xlim([-200, 1000])
axs[0].set_title('Logarithmic frequency scaling')
axs[
# Linear frequency scaling
= axs[1].contourf(EEG['times'][0], frex, eegpower, 40, cmap='viridis', vmin=-3, vmax=3)
cf2 1].set_xlim([-200, 1000])
axs[1].set_title('Linear frequency scaling')
axs[
plt.tight_layout() plt.show()
IMPORTANT TANGENT HERE ON Y-AXIS SCALING!!!
# Plotting
= plt.subplots(2, 2, figsize=(10, 8))
fig, axs
# Logarithmic frequency scaling
= axs[0, 0].contourf(EEG['times'][0], frex, eegpower, 40, cmap='jet', vmin=-3, vmax=3)
cf1 0, 0].set_yscale('log')
axs[0, 0].set_yticks(np.logspace(np.log10(min_freq), np.log10(max_freq), 6))
axs[0, 0].set_yticklabels(np.round(np.logspace(np.log10(min_freq), np.log10(max_freq), 6), 1))
axs[0, 0].set_xlim([-200, 1000])
axs[0, 0].set_title('Logarithmic frequency scaling')
axs[0, 0].set_ylabel('Frequency (Hz)')
axs[
# Linear frequency scaling
= axs[0, 1].contourf(EEG['times'][0], frex, eegpower, 40, cmap='jet', vmin=-3, vmax=3)
cf2 0, 1].set_xlim([-200, 1000])
axs[0, 1].set_title('Linear frequency scaling')
axs[
# WRONG Y-AXIS LABELS!!!!
= axs[1, 0].imshow(eegpower, aspect='auto', extent=[EEG['times'][0][0], EEG['times'][0][-1], frex[0], frex[-1]], cmap='jet', origin = "lower", vmin=-3, vmax=3)
im1 1, 0].set_xlim([-200, 1000])
axs[1, 0].set_title('WRONG Y-AXIS LABELS!!!!')
axs[1, 0].set_ylabel('Frequency (Hz)')
axs[1, 0].set_xlabel('Time (ms)')
axs[
# CORRECT Y-AXIS LABELS!!!!
= axs[1, 1].imshow(eegpower, aspect='auto', extent=[EEG['times'][0][0], EEG['times'][0][-1], 0, len(frex)], cmap='jet', origin = "lower", vmin=-3, vmax=3)
im2 1, 1].set_xlim([-200, 1000])
axs[1, 1].set_yticks(np.linspace(0, len(frex), 6))
axs[1, 1].set_yticklabels(np.round(np.logspace(np.log10(min_freq), np.log10(max_freq), 6), 1))
axs[1, 1].set_title('CORRECT Y-AXIS LABELS!!!!')
axs[1, 1].set_xlabel('Time (ms)')
axs[
plt.tight_layout() plt.show()
Figure 13.12
# Parameters for the wavelet
= 6 # Frequency of the sine wave
frequency = 500 # Note: should be the same as the data
srate = np.arange(-0.5, 0.5 + 1/srate, 1/srate) # Vector of time
time
# Make wavelet
= np.exp(2 * 1j * np.pi * frequency * time) * np.exp(-time**2 / (2 * (4 / (2 * np.pi * frequency))**2))
wavelet
# Plotting
= plt.subplots(2, 1, figsize=(10, 6))
fig, axs
# Good wavelet
0].plot(time, np.real(wavelet))
axs[0].set_xlim([-0.5, 0.5])
axs[0].set_ylim([-1, 1])
axs[0].set_title('Good.')
axs[
# Now make a wavelet that is too short
= 2
tooLowFrequency = np.exp(2 * 1j * np.pi * tooLowFrequency * time) * np.exp(-time**2 / (2 * (4 / (2 * np.pi * tooLowFrequency))**2))
wavelet
# Not good wavelet
1].plot(time, np.real(wavelet))
axs[1].set_xlim([-0.5, 0.5])
axs[1].set_ylim([-1, 1])
axs[1].set_xlabel('Time')
axs[1].set_title('Not good.')
axs[
plt.tight_layout() plt.show()
Figure 13.13
= 10
frequency = np.arange(-0.5, 0.5 + 1/srate, 1/srate)
time = [3, 7]
numcycles
= 'br'
wavecolors
# Plotting
= plt.figure(figsize=(12, 8))
fig
for i, nc in enumerate(numcycles):
# Make wavelet
= np.exp(2 * 1j * np.pi * frequency * time) * np.exp(-time**2 / (2 * (nc / (2 * np.pi * frequency))**2))
wavelet
# Time domain representation
2,2,i+1)
plt.subplot(
plt.plot(time, np.real(wavelet), wavecolors[i])-0.5, 0.5])
plt.xlim([-1, 1])
plt.ylim(['Time')
plt.xlabel(f'Wavelet at {frequency} Hz with {nc} cycles')
plt.title(
# Frequency domain representation
2,1,2)
plt.subplot(= 2 * np.abs(fft(wavelet))
fft_wav = np.linspace(0, srate/2, int(np.round(len(wavelet)/2)) + 1)
hz_wav len(hz_wav)], wavecolors[i])
plt.plot(hz_wav, fft_wav[:
212)
plt.subplot('Frequency (Hz)')
plt.xlabel(0, 50])
plt.xlim([0, 300])
plt.ylim([f'{numcycles[0]} cycles', f'{numcycles[1]} cycles'])
plt.legend([
plt.tight_layout() plt.show()
Figure 13.14
To generate this figure, go up to the code for figure 13.11 and uncomment the lines that define the width of the Gaussians for the Morlet wavelets:
s = 3/(2*np.pi*frex)
s = 10/(2*np.pi*frex)
Then, re-run the code for figure 13.11.
Figure 13.15
= np.logspace(np.log10(2), np.log10(80), 30)
frex = 500
srate = np.arange(-2, 2 + 1/srate, 1/srate)
time = len(time)
N = np.linspace(0, srate/2, int(np.floor(N/2)) + 1)
hz = np.zeros((3, len(frex)))
fwhm
for numcyclesi in range(3):
if numcyclesi == 0:
= np.repeat(3, len(frex))
numcycles elif numcyclesi == 1:
= np.repeat(10, len(frex))
numcycles else:
= np.logspace(np.log10(3), np.log10(10), len(frex))
numcycles
for fi in range(len(frex)):
# Make wavelet
= np.exp(2 * 1j * np.pi * frex[fi] * time) * np.exp(-time**2 / (2 * (numcycles[fi] / (2 * np.pi * frex[fi]))**2))
wavelet
# Take FFT of wavelet
= fft(wavelet)
fwave = np.abs(fwave[:len(hz)]) * 2
fwave
# Normalize power to [0 1]
= (fwave - np.min(fwave)) / np.max(fwave)
fwave
# Find left and right 1/2
= np.argmax(fwave)
peakx = np.argmin(np.abs(fwave[:peakx-2] - 0.5))
left5 = peakx + np.argmin(np.abs(fwave[peakx-1:] - 0.5)) - 1
right5
= hz[right5] - hz[left5]
fwhm[numcyclesi, fi]
# Plot one example of a wavelet's power spectrum and fwhm
if fi == int(np.ceil(len(frex)/2))-1 and numcyclesi == 2:
plt.figure()
# Plot power spectrum
'.-')
plt.plot(hz, fwave,
# Plot fwhm
'ro', markersize=10)
plt.plot(hz[left5], fwave[left5], 'ro', markersize=10)
plt.plot(hz[right5], fwave[right5], 0, fwave[left5]], 'r')
plt.plot([hz[left5], hz[left5]], [0, fwave[right5]], 'r')
plt.plot([hz[right5], hz[right5]], [
0, 30])
plt.xlim([0, 1])
plt.ylim(['Frequency (Hz)')
plt.xlabel('Normalized power')
plt.ylabel(
plt.show()
# Plot FWHM
plt.figure()0, :], '.-')
plt.plot(frex, fwhm[1, :], '.-')
plt.plot(frex, fwhm[2, :], '.-')
plt.plot(frex, fwhm[0, 80])
plt.xlim([0, 70])
plt.ylim(['Frequency (Hz)')
plt.xlabel('FWHM (Hz)')
plt.ylabel('3', '10', '3-10'])
plt.legend([
plt.show()
# Log-log plot of FWHM
plt.figure()0, :], '.-')
plt.plot(frex, fwhm[1, :], '.-')
plt.plot(frex, fwhm[2, :], '.-')
plt.plot(frex, fwhm['log')
plt.xscale('log')
plt.yscale(0]*0.8, frex[-1]*1.2])
plt.xlim([frex[min(fwhm)*0.8, np.max(fwhm)*1.2])
plt.ylim([np.round(np.logspace(np.log10(frex[0]), np.log10(frex[-1]), 6)))
plt.gca().set_xticks(np.round(10*np.logspace(np.log10(np.min(fwhm)), np.log10(np.max(fwhm)), 6))/10)
plt.gca().set_yticks(np.round(np.logspace(np.log10(frex[0]), np.log10(frex[-1]), 6)))
plt.gca().set_xticklabels(np.round(10*np.logspace(np.log10(np.min(fwhm)), np.log10(np.max(fwhm)), 6))/10)
plt.gca().set_yticklabels(np.'Frequency (Hz)')
plt.xlabel('FWHM (Hz)')
plt.ylabel('3', '10', '3-10'])
plt.legend([ plt.show()