I am analyzing functional MRI data with a sampling rate of 1 second (1 Hz). The frequency band that I am interested in is 0.01-0.2 Hz.
Regarding measurements, I am interested in computing (1) the signal’s temporal autocorrelation in the time-domain, and (2) the slope of a linear least-square fit in the log-log transformed frequency-domain.
Because temporal autocorrelation drastically increases to unrealistic values without bandpassing, I would like to bandpass the data.
Problem: My problem is that a bandpass filter that I constructed via the code below in Python introduces relatively heavy drops in power near the lowest (0.01 Hz) and highest (0.2 Hz) frequency that destroys the scale-free nature of the data.
# data = time-series data that I load for several subjects
sr = 1/sr # s to Hz
low = 0.01 # min freq
high = 0.2 # max freq
bandpass = sp.signal.butter(N=5, fs=sr, Wn=[low, high], btype="bandpass", analog=False, output="sos")
filtered_data = sp.signal.sosfilt(sos=bandpass, x=data, zi=None)
Within a neuroimaging preprocessing software, I have previously used the same bandpass range, that is, 0.01-0.2 Hz. Interestingly, the results of this neuroimaging software do not produce a loss in power near the lower and upper end of the frequency band, as observed here for sp.signal.butter
. I noticed that increasing the order
(=N
) option of sp.signal.butter
, such as from 1
to 5
, 10
or even higher slightly reduces this drop in power near the edges, but it does not disappear.
A comparison between non-bandpassed and bandpassed data (using scipy) is shown below.
Question: Is there a way to modify sp.signal.butter
or another way to bandpass data in Python without decreasing the power of the signal near the lower and upper frequency limits of the bandpass filter?
Update: Instead of using sp.signal.butter
, I tested a Chebyshev Type II filter via sp.signal.cheby2
and the following settings:
# Bandpass
sr = 1/sr # s to Hz
low = 0.01 # min freq
high = 0.2 # max freq
bandpass = sp.signal.cheby2(N=1, rs=0.001, Wn=[low, high],
btype="bandpass", analog=False,
output="sos", fs=sr)
row = sp.signal.sosfilt(sos=bandpass, x=row, zi=None)
This results in the following output. As can be seen, the roll-off now appears to be sharp, that is, the power no longer drops near the lowest and highest frequency. I am not sure if the selected paramters are reasonable from a methodological point of view. At least visually the results look good.
rs
is in decibels, so you're specifying $0.001\tt{dB}$ minimum attenuation in the stopband... basically you have an all-pass filter. $\endgroup$rs=0.001
look identical. Also, the temporal correlation withrs=0.001
is way too high, which makes sense given that this filter does not filter out anything. I tried your suggested broader frequency band, that is,Wn=[0.006, 0.27]
. But even that results in a loss of the scale-free power spectrum. Probably it is best only to apply bandpassing for the time-domain, but not for the frequency-domain (since I "cut" the power spectra to the desired band by sp.periodogram anyway). $\endgroup$