我正在寻找一种有效的方法来实现一个简单的滤波器,其中一个系数是时变的,由一个与输入信号长度相同的矢量指定。
以下是所需行为的简单实现:
def myfilter(signal, weights):
output = np.empty_like(weights)
val = signal[0]
for i in range(len(signal)):
val += weights[i]*(signal[i] - val)
output[i] = val
return output
weights = np.random.uniform(0, 0.1, (100,))
signal = np.linspace(1, 3, 100)
output = myfilter(signal, weights)
有没有办法用numpy或scipy更有效地做到这一点?
答案 0 :(得分:4)
您可以在循环开销中交换几个额外的操作:
import numpy as np
def myfilter(signal, weights):
output = np.empty_like(weights)
val = signal[0]
for i in range(len(signal)):
val += weights[i]*(signal[i] - val)
output[i] = val
return output
def vectorised(signal, weights):
wp = np.r_[1, np.multiply.accumulate(1 - weights[1:])]
sw = weights * signal
sw[0] = signal[0]
sws = np.add.accumulate(sw / wp)
return wp * sws
weights = np.random.uniform(0, 0.1, (100,))
signal = np.linspace(1, 3, 100)
print(np.allclose(myfilter(signal, weights), vectorised(signal, weights)))
在我的机器上,矢量化版本要快几倍。它使用你的递归方程的“封闭形式”解决方案。
编辑:对于非常长的信号/重量(例如100,000个样本),此方法因溢出而无效。在那种情况下你仍然可以使用以下技巧节省一点(在我的机器上超过50%),这有额外的好处,你不需要解决递推公式,只能反转它。
from scipy import linalg
def solver(signal, weights):
rw = 1 / weights[1:]
v = np.r_[1, rw, 1-rw, 0]
v.shape = 2, -1
return linalg.solve_banded((1, 0), v, signal)
这个技巧使用了这样一个事实:你的重复在形式上类似于只有一个非对称子对角线的矩阵上的高斯消元。它捎带在一个专门做这个的图书馆功能。
实际上,为此感到自豪。