繁体   English   中英

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

[英]How can I apply a vectorized function to the previous element of a numpy array?

我想应用这样的函数:

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

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

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

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]

正如我在对基本相同问题回答中所写,您不能:

除了显式的for循环之外,没有其他方法(通常)。 这是因为无法跨行并行化此任务(因为每一行都依赖于其他行)。

让这更难的是你可以很容易地产生混乱的行为,例如看似无辜的逻辑图x_{n+1} = r * x_n * (1 - x_{n-1})

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

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

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)

所以,可以有一个非循环版本,但它比其他任何东西都更令人好奇。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM