如何将矢量化函数应用于numpy数组的前一个元素?

时间:2018-02-28 15:11:30

标签: python arrays numpy vectorization

我想应用这样的函数:

s[i] = a*x[i] + (1 - a)*s[i-1]

其中sx都是相同长度的数组。

我不想使用for循环,因为这些数组非常大(> 50 mil)。我试过做这样的事情

def f(a,x):
    s = [0]*len(x)
    s[i] = a*x[i] + (1 - a)*s[i-1]
    return s

但当然i未定义,所以这不起作用。

有没有办法使用mapnumpy.apply_along_axis或其他一些矢量化方法执行此操作?

我没有遇到过将函数应用于数组的当前和前一个元素而不使用for循环的方法,这就是我想要了解的方法。

修改

要明确一点,这里的for循环实现有效,但我想避免

s = [0]*len(x)
a=0.45
for i in range(len(x)):
    s[i] = a*x[i] + (1-a)*s[i-1]

s[0] = x[0] # reset value of s[0]

2 个答案:

答案 0 :(得分:1)

正如我在answer to basically the same question写的那样,你不能:

  

除了明确的for循环外,没有其他方法(一般情况下)。   这是因为没有办法将这个任务并行化   行(因为每一行都依赖于其他行)。

     

更难以实现的是,您可以轻松生成chaotic behavior,例如看似无辜的外观   logistic mapx_{n+1} = r * x_n * (1 - x_{n-1})

     

如果你设法找到一个关闭的话,你只能找到解决方法   形式,基本上消除了复发关系。但这必须   为每个复发关系做,我很确定你不是   甚至保证存在封闭的形式......

答案 1 :(得分:0)

你可以避免循环,尽管矢量化更像是“计算每个值的所有内容”。

import numpy as np

# Loop
def fun(x, a):
    s = [0] * len(x)
    for i in range(len(x)):
        s[i] = a * x[i] + (1 - a) * s[i - 1]
    s[0] = x[0]
    return s

# Vectorized
def fun_vec(x, a):
    x = np.asarray(x, dtype=np.float32)
    n = np.arange(len(x))
    p = a * (1 - a) ** n
    # Trick from here: https://stackoverflow.com/q/49532575/1782792
    pz = np.concatenate((np.zeros(len(p) - 1, dtype=p.dtype), p))
    pp = np.lib.stride_tricks.as_strided(
        pz[len(p) - 1:], (len(p), len(p)),
        (p.strides[0], -p.strides[0]), writeable=False)
    t = x[np.newaxis] * pp
    s = np.sum(t, axis=1)
    s[0] = x[0]
    return s


x = list(range(1, 11))
a = 0.45
print(np.allclose(fun(x, a), fun_vec(x, a)))
# True

这种策略虽然采用O(n 2 )内存,但计算量更大。根据情况,由于并行性可以更快(我在TensorFlow中做了类似的事情以消除tf.while_loop以取得巨大成功),但在这种情况下它实际上更慢:

x = list(range(1, 101))
a = 0.45
%timeit fun(x, a)
# 31 µs ± 85.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit fun_vec(x, a)
# 147 µs ± 2.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

所以,可以有一个非循环版本,但它更像是一种好奇心。