import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hilbert, firls, firwin, filtfilt, lfilter, butter
from scipy.signal.windows import hamming
from scipy.fft import fft, ifft
from scipy.io import loadmat
import time as time_module
Chapter 14
Chapter 14
Analyzing Neural Time Series Data
Python code for Chapter 14 – 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 14.1
# Create cosine
= np.arange(0, 1.001, 0.001)
time = np.cos(2 * np.pi * 5 * time)
cosine
# Plot cosine and its Hilbert transform components
plt.figure()='cosine')
plt.plot(time, cosine, label5], np.real(hilbert(cosine[::5])), 'ko', label='real part') # plot every 5th point
plt.plot(time[::'r', label='imag part')
plt.plot(time, np.imag(hilbert(cosine)), 'm', label='angle')
plt.plot(time, np.angle(hilbert(cosine)), 0, 1])
plt.xlim(['Angle or amplitude')
plt.ylabel(
plt.legend() plt.show()
The FFT-based Hilbert transform
# Generate random numbers
= 21
n = np.random.randn(n)
randomnumbers
# Take FFT
= fft(randomnumbers)
f
# Create a copy that is multiplied by the complex operator
= 1j * f
complexf
# Find indices of positive and negative frequencies
= np.arange(1, np.floor(n / 2) + np.mod(n, 2)).astype(int)
posF = np.arange(np.ceil(n / 2) + 1 - np.mod(n, 2), n).astype(int)
negF
# Rotate Fourier coefficients
= f[posF] + -1j * complexf[posF]
f[posF] = f[negF] + 1j * complexf[negF]
f[negF]
# The next two lines are an alternative and slightly faster method.
# The book explains why this is equivalent to the previous two lines.
# f[posF] = f[posF]*2
# f[negF] = f[negF]*0
# Take inverse FFT
= ifft(f)
hilbertx
# Compare with scipy.signal.hilbert function
= hilbert(randomnumbers, axis=0)
hilbertm
# Plot results
plt.figure()211)
plt.subplot(abs(hilbertm), label='scipy.signal.hilbert function')
plt.plot(np.abs(hilbertx), 'ro', label='"manual" Hilbert')
plt.plot(np.
plt.legend()'magnitude of Hilbert transform')
plt.title(
212)
plt.subplot(='scipy.signal.hilbert function')
plt.plot(np.angle(hilbertm), label'ro', label='"manual" Hilbert')
plt.plot(np.angle(hilbertx),
plt.legend()'phase of Hilbert transform')
plt.title(
plt.tight_layout() plt.show()
Figure 14.2
# Load sample EEG data
= loadmat('../data/sampleEEGdata.mat')['EEG'][0, 0]
EEG
# Filter parameters
= EEG['srate'][0][0] / 2
nyquist = 4 # Hz
lower_filter_bound = 10 # Hz
upper_filter_bound = 0.2
transition_width = round(3 * (EEG['srate'][0][0] / lower_filter_bound)) + 1
filter_order
# Create the filter shape
= [0, (1 - transition_width) * lower_filter_bound, lower_filter_bound,
ffrequencies 1 + transition_width) * upper_filter_bound, nyquist] / nyquist
upper_filter_bound, (= [0, 0, 1, 1, 0, 0]
idealresponse = firls(filter_order, ffrequencies, idealresponse)
filterweights
# Apply the filter kernel to the data to obtain the band-pass filtered signal
= np.zeros((EEG['nbchan'][0][0], EEG['pnts'][0][0]))
filtered_data for chani in range(EEG['nbchan'][0][0]):
= filtfilt(filterweights, 1, EEG['data'][chani, :, 0])
filtered_data[chani, :]
# Apply Hilbert transform in correct and incorrect orientations
= hilbert(filtered_data.T).T
hilbert_oops = hilbert(filtered_data)
hilbert_yes # note these are opposite from Matlab
# Plot results
plt.figure()221)
plt.subplot('times'][0], np.angle(hilbert_yes[0, :].T), 'b')
plt.plot(EEG['correct matrix orientation')
plt.title('Time (ms)')
plt.xlabel('Phase angle (rad.)')
plt.ylabel(-1000, 1500])
plt.xlim([
222)
plt.subplot('times'][0], np.angle(hilbert_oops[0, :]), 'r')
plt.plot(EEG['incorrect matrix orientation')
plt.title('Time (ms)')
plt.xlabel('Phase angle (rad.)')
plt.ylabel(-1000, 1500])
plt.xlim([
223)
plt.subplot('times'][0], np.real(hilbert_yes[0, :]), 'b')
plt.plot(EEG['correct matrix orientation')
plt.title('Time (ms)')
plt.xlabel('Amplitude')
plt.ylabel(-1000, 1500])
plt.xlim([
224)
plt.subplot('times'][0], np.real(hilbert_oops[0, :]), 'r')
plt.plot(EEG['incorrect matrix orientation')
plt.title('Time (ms)')
plt.xlabel('Amplitude')
plt.ylabel(-1000, 1500])
plt.xlim([
plt.tight_layout() plt.show()
Figure 14.3
# Define parameters
= 20 # in Hz
center_freq = 6 # Hz +/- the center frequency
filter_frequency_spread = 4 # number of wavelet cycles
wavelet_frequency_spread
# Create wavelet
= np.arange(-1000 / EEG['srate'][0][0] / 10, 1000 / EEG['srate'][0][0] / 10 + 1 / EEG['srate'][0][0], 1 / EEG['srate'][0][0])
time = np.exp(2 * 1j * np.pi * center_freq * time) * np.exp(-time**2 / (2 * (wavelet_frequency_spread / (2 * np.pi * center_freq))**2))
wavelet = (wavelet - np.mean(wavelet)) / np.std(wavelet) # z-score
wavelet
# Compute its power spectrum
= np.abs(fft(wavelet))
fft_wavelet = fft_wavelet / np.max(fft_wavelet) # normalized to 1.0 for visual comparison ease
fft_wavelet = np.linspace(0, nyquist, int(len(time) / 2) + 1)
hz_wavelet
# Construct filter kernel
= 0.2
transition_width = [0, (1 - transition_width) * (center_freq - filter_frequency_spread),
ffrequencies - filter_frequency_spread, center_freq + filter_frequency_spread,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread), nyquist] / nyquist
(= [0, 0, 1, 1, 0, 0]
idealresponse = firls(201, ffrequencies, idealresponse)
filterweights = (filterweights - np.mean(filterweights)) / np.std(filterweights) # z-score
filterweights # Also compute weights using firwin
= firwin(201, [center_freq - filter_frequency_spread, center_freq + filter_frequency_spread] / nyquist, pass_zero=False)
filterweights1 = (filterweights1 - np.mean(filterweights1)) / np.std(filterweights1) # z-score
filterweights1
# Compute power spectrum of filter kernel
= np.abs(fft(filterweights))
fft_filtkern = fft_filtkern / np.max(fft_filtkern) # normalized to 1.0 for visual comparison ease
fft_filtkern = np.abs(fft(filterweights1))
fft_filtkern1 = fft_filtkern1 / np.max(fft_filtkern1) # normalized to 1.0 for visual comparison ease
fft_filtkern1
= np.linspace(0, nyquist, 101) # list of frequencies in Hz corresponding to filter kernel
hz_filtkern
# Plot wavelet and filter kernel
plt.figure()211)
plt.subplot(='wavelet')
plt.plot(np.real(wavelet), label'r', label='filter kernel')
plt.plot(filterweights, 0, 200])
plt.xlim([-5, 5])
plt.ylim(['Time')
plt.xlabel(
plt.legend()
# Plot power spectra
212)
plt.subplot(int(np.ceil(len(fft_wavelet) / 2))], label='wavelet')
plt.plot(hz_wavelet, fft_wavelet[:int(np.ceil(len(fft_filtkern) / 2))], 'r', label='filter kernel')
plt.plot(hz_filtkern, fft_filtkern[:0, 140])
plt.xlim([-.1, 1.1])
plt.ylim(['Frequency (Hz)')
plt.xlabel(
plt.legend()
plt.tight_layout() plt.show()
Figure 14.4
# Define parameters
= 20 # in Hz
center_freq = 10 # Hz +/- the center frequency
filter_frequency_spread_wide
# Construct filter kernel
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread_wide),
ffrequencies - filter_frequency_spread_wide, center_freq + filter_frequency_spread_wide,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread_wide), nyquist] / nyquist
(= [0, 0, 1, 1, 0, 0]
idealresponse = firls(201, ffrequencies, idealresponse)
filterweightsW = (filterweightsW - np.mean(filterweightsW)) / np.std(filterweightsW) # z-score
filterweightsW
# Compute power spectrum of filter kernel
= np.abs(fft(filterweightsW))
fft_filtkern = fft_filtkern / np.max(fft_filtkern) # normalized to 1.0 for visual comparison ease
fft_filtkern
# Plot ideal response and best fit
plt.figure()* nyquist, idealresponse, 'r', label='ideal')
plt.plot(ffrequencies int(np.ceil(len(fft_filtkern) / 2))], 'b', label='best fit')
plt.plot(hz_filtkern, fft_filtkern[:-.1, 1.1])
plt.ylim([0, nyquist])
plt.xlim([
plt.legend() plt.show()
Figure 14.5
# Define parameters
= 20 # in Hz
center_freq = 10 # Hz +/- the center frequency
filter_frequency_spread_wide = 2 # Hz +/- the center frequency
filter_frequency_spread_naro
# Construct wide filter kernel
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread_wide),
ffrequencies - filter_frequency_spread_wide, center_freq + filter_frequency_spread_wide,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread_wide), nyquist] / nyquist
(= [0, 0, 1, 1, 0, 0]
idealresponse = firls(201, ffrequencies, idealresponse)
filterweightsW = (filterweightsW - np.mean(filterweightsW)) / np.std(filterweightsW) # z-score
filterweightsW
# Construct narrow filter kernel
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread_naro),
ffrequencies - filter_frequency_spread_naro, center_freq + filter_frequency_spread_naro,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread_naro), nyquist] / nyquist
(= [0, 0, 1, 1, 0, 0]
idealresponse = firls(201, ffrequencies, idealresponse)
filterweightsN = (filterweightsN - np.mean(filterweightsN)) / np.std(filterweightsN) # z-score
filterweightsN
# Plot filter kernels and their Hilbert transforms
plt.figure()211)
plt.subplot(= np.abs(fft(filterweightsW))
fft_filtkern = fft_filtkern / np.max(fft_filtkern) # normalized to 1.0 for visual comparison ease
fft_filtkern int(np.ceil(len(fft_filtkern) / 2))])
plt.plot(hz_filtkern, fft_filtkern[:
= np.abs(fft(filterweightsN))
fft_filtkern = fft_filtkern / np.max(fft_filtkern) # normalized to 1.0 for visual comparison ease
fft_filtkern int(np.ceil(len(fft_filtkern) / 2))], 'r')
plt.plot(hz_filtkern, fft_filtkern[:0, 140])
plt.xlim([-.1, 1.1])
plt.ylim(['10 Hz width', '2 Hz width'])
plt.legend([
223)
plt.subplot(
plt.plot(filterweightsW)'r')
plt.plot(filterweightsN, 0, 200])
plt.xlim([-4, 7])
plt.ylim(['10 Hz spread', '2 Hz spread'])
plt.legend(['Time')
plt.xlabel(
224)
plt.subplot(abs(hilbert(filterweightsW)), 'b')
plt.plot(np.abs(hilbert(filterweightsN)), 'r')
plt.plot(np.0, 200])
plt.xlim([-4, 7])
plt.ylim(['10 Hz spread', '2 Hz spread'])
plt.legend(['Time')
plt.xlabel(
plt.tight_layout() plt.show()
Figure 14.6
# Plot filter kernels and their power spectra
plt.figure()211)
plt.subplot(='firwin')
plt.plot(filterweights1, label'r', label='firls')
plt.plot(filterweights, 0, 200])
plt.xlim(['Time')
plt.xlabel(
plt.legend()
212)
plt.subplot(int(np.ceil(len(fft_filtkern1) / 2))], label='firwin')
plt.plot(hz_filtkern, fft_filtkern1[:int(np.ceil(len(fft_filtkern) / 2))], 'r', label='firls')
plt.plot(hz_filtkern, fft_filtkern[:0, 140])
plt.xlim([-.1, 1.1])
plt.ylim(['Frequency (Hz)')
plt.xlabel(
plt.legend()
plt.tight_layout()
plt.show()
# This next figure shows that the filter kernel computed by firwin is the same as
# that produced by firls if you set the transition zone to zero and then
# smooth the filter kernel with a Hamming window. In the plot, the red and magenta
# lines overlap, which is why you don't see the firwin filter kernel. You can
# subtract them and show that the difference (which is due to re-scaling
# of the kernel after windowing) is 3-4 orders of magnitude smaller than
# the kernel itself, hence, nearly identical.
= center_freq - filter_frequency_spread
freqL = center_freq + filter_frequency_spread
freqU
= [0, freqL, freqL, freqU, freqU, nyquist] / nyquist
ffrequencies = firls(201, ffrequencies, idealresponse)
filterweights = firwin(201, [freqL, freqU] / nyquist, pass_zero=False)
filterweights1
plt.figure()211)
plt.subplot(='firls')
plt.plot(filterweights, label'r.', label='firwin')
plt.plot(filterweights1, * hamming(len(filterweights)), 'm', label='firls with hamming window')
plt.plot(filterweights
plt.legend() plt.show()
Figure 14.7a
# Define parameters
= 60 # in Hz
center_freq = 10 # Hz +/- the center frequency
filter_frequency_spread_wide
# Construct filter kernel
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread_wide),
ffrequencies - filter_frequency_spread_wide, center_freq + filter_frequency_spread_wide,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread_wide), nyquist] / nyquist
(= [0, 0, 1, 1, 0, 0]
idealresponse = firls(201, ffrequencies, idealresponse)
filterweightsW = (filterweightsW - np.mean(filterweightsW)) / np.std(filterweightsW) # z-score
filterweightsW
# Compute power spectrum of filter kernel
= np.abs(fft(filterweightsW))
fft_filtkern = fft_filtkern / np.max(fft_filtkern) # normalized to 1.0 for visual comparison ease
fft_filtkern
# Plot ideal response and best fit
plt.figure()* nyquist, idealresponse, 'r', label='ideal')
plt.plot(ffrequencies int(np.ceil(len(fft_filtkern) / 2))], 'b', label='best fit')
plt.plot(hz_filtkern, fft_filtkern[:-.1, 1.1])
plt.ylim([0, nyquist])
plt.xlim([
plt.legend()
# Calculate and display SSE (sum of squared errors)
= [np.argmin(np.abs(hz_filtkern - f)) for f in ffrequencies * nyquist]
freqsidx = np.sum((idealresponse - fft_filtkern[freqsidx])**2)
sse f'SSE: {sse}')
plt.title(
plt.show()
# Define parameters
= 60 # in Hz
center_freq = 15 # Hz +/- the center frequency
filter_frequency_spread_wide
# Construct filter kernel
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread_wide),
ffrequencies - filter_frequency_spread_wide, center_freq + filter_frequency_spread_wide,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread_wide), nyquist] / nyquist
(= [0, 0, 1, 1, 0, 0]
idealresponse = firls(201, ffrequencies, idealresponse)
filterweightsW = (filterweightsW - np.mean(filterweightsW)) / np.std(filterweightsW) # z-score
filterweightsW
# Compute power spectrum of filter kernel
= np.abs(fft(filterweightsW))
fft_filtkern = fft_filtkern / np.max(fft_filtkern) # normalized to 1.0 for visual comparison ease
fft_filtkern
# Plot ideal response and best fit
plt.figure()* nyquist, idealresponse, 'r', label='ideal')
plt.plot(ffrequencies int(np.ceil(len(fft_filtkern) / 2))], 'b', label='best fit')
plt.plot(hz_filtkern, fft_filtkern[:-.1, 1.1])
plt.ylim([0, nyquist])
plt.xlim([
plt.legend()
# Calculate and display SSE (sum of squared errors)
= [np.argmin(np.abs(hz_filtkern - f)) for f in ffrequencies * nyquist]
freqsidx = np.sum((idealresponse - fft_filtkern[freqsidx])**2)
sse f'SSE: {sse}')
plt.title( plt.show()
Figure 14.7b
# Define parameters
= np.linspace(5, 80, 60)
centerfreqs = np.linspace(0.01, 0.2, 40)
transwidths = np.linspace(0.05, 0.3, 40)
filterwidths
# Initialize SSE matrix for transition widths
= np.zeros((len(centerfreqs), len(transwidths)))
sse for centfreqi, center_freq in enumerate(centerfreqs):
for transwidi, transition_width in enumerate(transwidths):
= center_freq * 0.2
filter_frequency_spread_wide
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread_wide),
ffrequencies - filter_frequency_spread_wide, center_freq + filter_frequency_spread_wide,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread_wide), nyquist] / nyquist
(= firls(201, ffrequencies, idealresponse)
filterweightsW = (filterweightsW - np.mean(filterweightsW)) / np.std(filterweightsW) # z-score
filterweightsW
= np.abs(fft(filterweightsW))
fft_filtkern = fft_filtkern / np.max(fft_filtkern)
fft_filtkern = [np.argmin(np.abs(hz_filtkern - f)) for f in ffrequencies * nyquist]
freqsidx
= np.sum((idealresponse - fft_filtkern[freqsidx])**2)
sse[centfreqi, transwidi]
# Plot SSE for transition widths
plt.figure()40, cmap='viridis')
plt.contourf(transwidths, centerfreqs, sse, 'Transition Widths')
plt.xlabel('Center Frequencies')
plt.ylabel(0, 1])
plt.clim([
plt.colorbar()'SSE for Transition Widths')
plt.title(
plt.show()
# Initialize SSE matrix for filter widths
= np.zeros((len(centerfreqs), len(filterwidths)))
sse for centfreqi, center_freq in enumerate(centerfreqs):
for filterwidthi, filter_width in enumerate(filterwidths):
= center_freq * filter_width
filter_frequency_spread_wide = 0.2
transition_width
= [0, (1 - transition_width) * (center_freq - filter_frequency_spread_wide),
ffrequencies - filter_frequency_spread_wide, center_freq + filter_frequency_spread_wide,
center_freq 1 + transition_width) * (center_freq + filter_frequency_spread_wide), nyquist] / nyquist
(= firls(201, ffrequencies, idealresponse)
filterweightsW = (filterweightsW - np.mean(filterweightsW)) / np.std(filterweightsW) # z-score
filterweightsW
= np.abs(fft(filterweightsW))
fft_filtkern = fft_filtkern / np.max(fft_filtkern)
fft_filtkern = [np.argmin(np.abs(hz_filtkern - f)) for f in ffrequencies * nyquist]
freqsidx
= np.sum((idealresponse - fft_filtkern[freqsidx])**2)
sse[centfreqi, filterwidthi]
# Plot SSE for filter widths
plt.figure()40, cmap='viridis')
plt.contourf(filterwidths, centerfreqs, sse, 'Filter Widths (% Center Freq.)')
plt.xlabel('Center Frequencies')
plt.ylabel(0, 1])
plt.clim([
plt.colorbar()'SSE for Filter Widths')
plt.title( plt.show()
Figure 14.8
# Define parameters
= 10
center_freq = 4 # Hz +/- the center frequency
freqspread = 0.10
transwid = [0, (1 - transwid) * (center_freq - freqspread), (center_freq - freqspread),
ffrequencies + freqspread), (1 + transwid) * (center_freq + freqspread), nyquist] / nyquist
(center_freq
# Data to filter
= EEG['data'][46, :, 0]
data2filter = firls(201, ffrequencies, idealresponse)
filterweights
# Filter the data using filtfilt and convolve
= filtfilt(filterweights, 1, data2filter)
filter_result = np.convolve(data2filter, filterweights, 'same')
convol_result
# Plot the results
plt.figure()'times'][0], filter_result, label='filtfilt')
plt.plot(EEG['times'][0], convol_result, 'r', label='conv')
plt.plot(EEG[-200, 1000])
plt.xlim(['Time (ms)')
plt.xlabel('Amplitude (μV)')
plt.ylabel(
plt.legend() plt.show()
Figure 14.9
# Define parameters
= 10 # in Hz
center_freq = EEG['srate'][0][0] / 2
nyquist
# Create short sine wave
= np.arange(-2000 / EEG['srate'][0][0] / 10, 2000 / EEG['srate'][0][0] / 10 + 1 / EEG['srate'][0][0], 1 / EEG['srate'][0][0])
time = np.cos(2 * np.pi * center_freq * time) * np.exp(-time**2 / (2 * (3 / (2 * np.pi * center_freq))**2))
wavelet
= 4 # Hz +/- the center frequency
freqspread = 0.10
transwid
# Construct filter kernels
= [0, (1 - transwid) * (center_freq - freqspread), (center_freq - freqspread),
ffrequencies + freqspread), (1 + transwid) * (center_freq + freqspread), nyquist] / nyquist
(center_freq = [0, 0, 1, 1, 0, 0]
idealresponse = firls(251, ffrequencies, idealresponse)
filterweights
# Apply forward and reverse filtering
= lfilter(filterweights, 1, wavelet)
forward_filt = lfilter(filterweights, 1, forward_filt[::-1])
reverse_filt = reverse_filt[::-1] # Reverse time again
final_filt_result
# Plot the results
plt.figure()='signal')
plt.plot(wavelet, label'r', label='forward filter')
plt.plot(forward_filt, 'm', label='reverse filter')
plt.plot(final_filt_result, 0, len(wavelet)])
plt.xlim([
plt.legend() plt.show()
Figure 14.10
# 5th-order Butterworth filter
= butter(5, [(center_freq - filter_frequency_spread) / nyquist, (center_freq + filter_frequency_spread) / nyquist], btype='bandpass')
butterB, butterA = filtfilt(butterB, butterA, data2filter)
butter_filter
# Plot the real part of the filtered signal
plt.figure()211)
plt.subplot('times'][0], filter_result, label='FIR')
plt.plot(EEG['times'][0], butter_filter, 'r', label='Butterworth')
plt.plot(EEG[-200, 1000])
plt.xlim(['Time (ms)')
plt.xlabel('Amplitude (μV)')
plt.ylabel(
plt.legend()
# Now plot phases
212)
plt.subplot('times'][0], np.angle(hilbert(filter_result)), label='FIR')
plt.plot(EEG['times'][0], np.angle(hilbert(butter_filter)), 'r', label='Butterworth')
plt.plot(EEG[-200, 1000])
plt.xlim(['Time (ms)')
plt.xlabel('Phase angles (rad.)')
plt.ylabel(
plt.legend()
plt.tight_layout() plt.show()
Figure 14.12
# Define parameters
= [0, 0]
elap_time = 100
num_iter
= 4 # Hz +/- the center frequency
freqspread = 20
center_freq = 0.15
transwid
# Construct filter kernel
= [0, (1 - transwid) * (center_freq - freqspread), (center_freq - freqspread),
ffrequencies + freqspread), (1 + transwid) * (center_freq + freqspread), nyquist] / nyquist
(center_freq = [0, 0, 1, 1, 0, 0]
idealresponse = firls(3 * round(EEG['srate'][0][0] / (center_freq - freqspread)) + 1, ffrequencies, idealresponse)
filterweights
# Concatenated filtering
for i in range(num_iter):
= time_module.time()
start_time = EEG['data'][46, :, :].flatten('F')
data2filter_cat = np.reshape(filtfilt(filterweights, 1, data2filter_cat), (EEG['pnts'][0][0], EEG['trials'][0][0]), 'F')
filtdat_cat 0] += time_module.time() - start_time
elap_time[
# Separated filtering
for i in range(num_iter):
= time_module.time()
start_time = EEG['data'][46, :, :]
data2filter_sep = np.zeros_like(data2filter_sep)
filtdat_sep for triali in range(EEG['trials'][0][0]):
= filtfilt(filterweights, 1, data2filter_sep[:, triali])
filtdat_sep[:, triali] 1] += time_module.time() - start_time
elap_time[
= np.array(elap_time) / num_iter
elap_time
# Plot
plt.figure()'times'][0], np.mean(filtdat_cat, axis=1), label='concatenated')
plt.plot(EEG['times'][0], np.mean(filtdat_sep, axis=1), 'r', label='separated')
plt.plot(EEG[-1000, 1500])
plt.xlim([
plt.legend()
plt.show()
plt.figure()0, 1], elap_time)
plt.bar([0, 1], ['Concatenated', 'Separated'])
plt.xticks(['Time (s)')
plt.ylabel(f'Speed increase of {elap_time[1] / elap_time[0]:.2f}!')
plt.title( plt.show()