Professional Documents
Culture Documents
Alexander Zhigalov / Dept. of CS, University of Helsinki and Dept. of NBE, Aalto University
Time series analysis in neuroscience 2
Outline / overview
Section 1. Signal smoothing
Section 2. Amplitude spectra
Section 3. Time and frequency domains
Section 4. Filter design
Section 5. Frequency response
Section 6. Padding
Time series analysis in neuroscience 3
What is smoothing?
# signal
x = 0.5 * np.sin(2 * np.pi * 5 * t) + \
0.2 * np.sin(2 * np.pi * 10 * t) + \
0.1 * np.sin(2 * np.pi * 20 * t) + \
0.5 * np.random.randn(N)
X = np.abs(fft(x)) / N
# smoothing, M samples
M = 20
y = do_smoothing(x, M)
Y = np.abs(fft(y)) / N
See, “L06_smoothing.py”
N = len(x)
y = np.zeros(N)
# average
for i in range(0, (N-M)):
y[i+M] = np.sum(x[i:(i+M)]) / M
return y
See, “L06_smoothing.py”
# signal
x = 0.5 * np.sin(2 * np.pi * 5 * t) + \
0.2 * np.sin(2 * np.pi * 10 * t) + \
0.1 * np.sin(2 * np.pi * 20 * t) + \
0.5 * np.random.randn(N)
X = np.abs(fft(x)) / N
# smoothing, M samples
M = 20
y = do_smoothing(x, M)
Y = np.abs(fft(y)) / N
# convolution
w = np.ones(M) / M
u = do_convolution(x, w)
u = u[0:N]
U = np.abs(fft(y))
See, “L06_convolution_square_window.py”
# init
N = len(x)
M = len(w)
# add zeros
x = np.concatenate((np.zeros(M-1), x,
np.zeros(M-1)))
u = np.zeros(N+M-1)
# convolution
for i in range(0, (N+M-1)):
u[i] = np.sum(x[i:(i+M)] * w[::-1])
return y
See, “L06_convolution_square_window.py”
Spectra (1/2)
# signal
x = 0.5 * np.sin(2 * np.pi * 2 * t) + \
0.2 * np.sin(2 * np.pi * 5 * t) + \
0.1 * np.sin(2 * np.pi * 10 * t)
X = np.abs(fft(x)) / N
# fourier transform
fx = fft(x)
# remove 5 Hz
f0 = 5
fx[[f0, -f0]] = 0 # [f0, nFFT-f0]
See, “L06_spectra_1.py”
Spectra (2/2)
# signal
x = 0.5 * np.sin(2 * np.pi * 5 * t) + \
0.2 * np.sin(2 * np.pi * 10 * t) + \
0.1 * np.sin(2 * np.pi * 20 * t) + \
0.5 * np.random.randn(N)
X = np.abs(fft(x)) / N
# fourier transform
fx = fft(x)
# remove above 25 Hz
f0 = 25
fx[np.arange(f0, nFFT-f0)] = 0
See, “L06_spectra_2.py”
Frequency domain
# fourier transform
fx = fft(x)
# remove frequencies above f0
fx[np.arange(f0, nFFT-f0)] = 0
# inverse fourier transform
y = ifft(fx)
Time domain
# design filter
[b, a] = signal.butter(4, f0 / (fs/2), 'low')
# apply filter
u = signal.filtfilt(b, a, x)
See, “L06_time_frequency_domains.py”
Applying filter
# zero-phase filtering
y = signal.filtfilt(b, a, x)
# tranditional filtering
u = signal.lfilter(b, a, x)
# or in compact form
z = lfilter(b, a, lfilter(b, a, x)[::-1])[::-1]
See, “L06_apply_filter.py”
# get length
N = len(x)
M = max(len(b), len(a))
# init
y = np.zeros(N)
# convolution
for i in range(M, N):
y[i] = np.sum(b * x[i:(i-M):-1]) –
np.sum(a[1:] * y[(i-1):(i-M):-1])
return y
See, “L06_apply_filter.py”
Filter parameters
Input parameters
• “butter” – Butterworth filter design (way to design filter)
• “4” – filter order
# design filter
[b, a] = signal.butter(4, f0 / (fs/2), 'low') • “f0” – cutoff frequency
• “fs/2” – sampling rate
• “low” – low-pass filter (type of filter)
Output parameters
• “b, a” – filter coefficients
…
delay delay delay
bq bq ap
xn–q xn–q yn–p
https://se.mathworks.com/help/signal/ref/dfilt.html
# low-pass filter
a = 1
b = signal.firwin(numtaps=n, cutoff=fc)
y = signal.lfilter(b, a, x)
# frequency response
w, h = signal.freqz(b, a, (fs//2))
# amplitude
ampl = np.abs(h)
# phase
phase = np.unwrap(np.angle(h))
See, “L06_fir_filter.py”
# low-pass filter
[b, a] = signal.butter(n, fc, 'low')
y = signal.lfilter(b, a, x)
# frequency response
w, h = signal.freqz(b, a, (fs//2))
# amplitude
ampl = np.abs(h)
# phase
phase = np.unwrap(np.angle(h))
See, “L06_iir_filter.py”
# design filter
n1 = 65 # FIR filter order
n2 = 4 # IIR filter order
# impulse response
p = np.zeros(200)
p[0] = 1
resp = signal.lfilter(b, a, p)
See, “L06_fir_vs_iir_filter.py”
Impulse response
# impulse response
p = np.zeros(200)
p[0] = 1
h = signal.lfilter(b, a, p)
# convolution
u = signal.convolve(x, h)
u = u[0:N]
See, “L06_impulse_response.py”
See, “L06_frequency_response_ideal.py”
See, “L06_frequency_response_real.py”
See, “L06_frequency_response_data.py”
# band-pass filter
n = 2
[b, a] = signal.butter(n,
[9.5/(fs/2), 10.5/(fs/2)], 'bandpass')
See, “L06_narrowband_filter.py”
# band-pass filter
n = 8
[b, a] = signal.butter(n,
[9.5/(fs/2), 10.5/(fs/2)], 'bandpass')
See, “L06_narrowband_filter.py”
Section 6. Padding
Time series analysis in neuroscience 29
Padding (1/2)
How to supress the “border” effects?
# design filter
fc = f0 / (fs/2) # cutoff frequency
# low-pass filter
n = 16
[b, a] = signal.butter(n, fc, 'lowpass')
y = signal.filtfilt(b, a, x)
# padding length
L = fs // f0
# zeros-padding
u = np.concatenate((np.zeros(L), x,
np.zeros(L)))
u = signal.filtfilt(b, a, u)
u = u[L:(N+L)]
See, “L06_padding.py”
Section 6 Padding
Time series analysis in neuroscience 30
Padding (2/2)
How to supress the “border” effects?
# design filter
fc = f0 / (fs/2) # cutoff frequency
# low-pass filter
n = 16
[b, a] = signal.butter(n, fc, 'lowpass')
y = signal.filtfilt(b, a, x)
# padding length
L = fs // f0
# signal-mirroring
u = np.concatenate((x[L:0:-1], x,
x[N:(N-L-1):-1]))
u = signal.filtfilt(b, a, u)
u = u[L:(N+L)]
See, “L06_padding.py”
Section 6 Padding
Time series analysis in neuroscience 31
Literature
Time series analysis in neuroscience 32
• Data analysis
- Downey A., “Think DSP: Digital Signal Processing in Python”, see materials/L05_thinkdsp.pdf
Literature