在Python / Numpy

时间:2017-01-26 23:05:05

标签: python numpy scipy

我正在寻找一种有效的方法来实现一个简单的滤波器,其中一个系数是时变的,由一个与输入信号长度相同的矢量指定。

以下是所需行为的简单实现:

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更有效地做到这一点?

1 个答案:

答案 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)

这个技巧使用了这样一个事实:你的重复在形式上类似于只有一个非对称子对角线的矩阵上的高斯消元。它捎带在一个专门做这个的图书馆功能。

实际上,为此感到自豪。