我非常精通统计学,但不是真正的数字信号过滤。我有一个数据场景,我希望能够很容易地滤除一些已知频带的噪声(人体脉冲),但是我在使用 scipy.signal 库中的标准工具时遇到了很多麻烦,并认为我一定是误解了如何设计数字滤波器。到目前为止,我在这里有一个笔记本可以完成我的探索,但要点是标准的 scipy 滤波器似乎会在我的信号开始和结束时造成大的失真,其精确行为取决于噪声信号的相位我'我试图减去。以防万一上面的活页夹链接失效,我还将包括以下一些关键点:
首先生成一些与我的真实数据相似的合成数据:
#generate time vector
samples_per_sec = 10.0
total_time = 40.0
time = np.linspace(0, total_time, int(total_time*samples_per_sec))
#generate the pulse signal
pulse_hz = 1.0
pulse_phase = np.radians(0)
pulse = np.sin(time*(2*np.pi)*pulse_hz - pulse_phase)
#generate the BOLD signal (just something that goes up then down)
dist = stats.beta(2, 2)
bold = dist.pdf((time-10)/20) / 10.0 # division by 10 to make bold a small signal
#combine
pulse_plus_bold = pulse+bold
plt.plot(time, pulse_plus_bold);
尝试一阶黄油:
#1st order butterworth filter in ba mode
ba1 = signal.butter(
output = 'ba'
, N = 1 #needs to be low if using output='ba', else use output='sos' and sosfiltfilt
, Wn = [0.5,1.5]
, btype = 'bandstop'
, fs = samples_per_sec
)
filtered_ba1_nopad = signal.filtfilt(
b = ba1[0]
, a = ba1[1]
, x = pulse_plus_bold
, padtype = None
)
plt.plot(time, filtered_ba1_nopad, 'b');
plt.plot(time, bold, 'r--');
plt.legend(['Filtered', 'Expected'], loc=(1.04,.5));
具有均匀填充的一阶 Butterworth:
filtered_ba1_pad_even = signal.filtfilt(
b = ba1[0]
, a = ba1[1]
, x = pulse_plus_bold
, method = 'pad'
, padtype = 'even'
)
plt.plot(time, filtered_ba1_pad_even, 'b');
plt.plot(time, bold, 'r--');
plt.legend(['Filtered', 'Expected'], loc=(1.04,.5));
具有奇数填充的一阶 Butterworth:
filtered_ba1_pad_odd = signal.filtfilt(
b = ba1[0]
, a = ba1[1]
, x = pulse_plus_bold
, method = 'pad'
, padtype = 'odd'
)
plt.plot(time, filtered_ba1_pad_odd, 'b');
plt.plot(time, bold, 'r--');
plt.legend(['Filtered', 'Expected'], loc=(1.04,.5));
后者看起来真的很好!但是在玩了之后,我发现奇数或偶数(或任何一个)填充效果更好似乎取决于被过滤掉的信号的相位。例如,虽然上面使用奇数填充获得了出色的滤波,但这是相同的场景,但在脉冲信号中添加了相移,从而产生奇数和偶数的边缘伪影:
phase = np.radians(45)
pulse_shifted = np.sin(time*(2*np.pi)*pulse_hz - phase)
pulse_shifted_plus_bold = pulse_shifted+bold
filtered_shifted_ba1_pad_odd = signal.filtfilt(
b = ba1[0]
, a = ba1[1]
, x = pulse_shifted_plus_bold
, method = 'pad'
, padtype = 'odd'
)
filtered_shifted_ba1_pad_even = signal.filtfilt(
b = ba1[0]
, a = ba1[1]
, x = pulse_shifted_plus_bold
, method = 'pad'
, padtype = 'even'
)
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(5, 3))
axes[0].plot(time, filtered_shifted_ba1_pad_odd, 'b')
axes[0].plot(time, bold, 'r--')
axes[1].plot(time, filtered_shifted_ba1_pad_even, 'b')
axes[1].plot(time, bold, 'r--')
fig.tight_layout()
plt.title('Odd (left) and Even (right)')
plt.legend(['Filtered', 'Expected'], loc=(1.04,.5));
我还尝试了“gust”填充方法以及高阶滤波器(当然使用 sos),并在我尝试过的所有内容中观察到相同的相位相关边缘伪影。有小费吗?