python – 迭代使用自己的输出的数组的最佳方法

python – 迭代使用自己的输出的数组的最佳方法,第1张

概述首先,我想为措辞严厉的标题道歉 – 我目前无法想到一个更好的方式来表达它.基本上,我想知道是否有更快的方法在 Python中实现数组 *** 作,其中每个 *** 作以迭代方式依赖于先前的输出(例如,前向差分 *** 作,过滤等).基本上, *** 作的形式如下: for n in range(1, len(X)): Y[n] = X[n] + X[n - 1] + Y[n-1] 其中X是值数组,Y是输出.在这种情况下, 首先,我想为措辞严厉的标题道歉 – 我目前无法想到一个更好的方式来表达它.基本上,我想知道是否有更快的方法在 Python中实现数组 *** 作,其中每个 *** 作以迭代方式依赖于先前的输出(例如,前向差分 *** 作,过滤等).基本上, *** 作的形式如下:

for n in range(1,len(X)):    Y[n] = X[n] + X[n - 1] + Y[n-1]

其中X是值数组,Y是输出.在这种情况下,假设Y [0]在上述循环之前是已知的或单独计算的.我的问题是:是否有NumPy功能来加速这种自引用循环?这是几乎所有脚本的主要瓶颈.我知道NumPy例程可以从C例程执行中受益,所以我很好奇是否有人知道任何有助于此的numpy例程.否则,是否有更好的方法来编程这个循环(在Python中),这将加速其对大数组大小的执行? (> 500,000个数据点).

解决方法 访问单个NumPy数组元素或(元素 – )迭代NumPy数组很慢(就像真的很慢).如果你想对NumPy数组进行手动迭代:就是不要这样做!

但你有一些选择.最简单的方法是将数组转换为python列表并迭代列表(听起来很愚蠢,但请留在我身边 – 我将在答案1的末尾提供一些基准测试):

X = X.toList()Y = Y.toList()for n in range(1,len(X)):    Y[n] = X[n] + X[n - 1] + Y[n-1]

如果您还对列表使用直接迭代,则可能更快:

X = X.toList()Y = Y.toList()for IDx,(Y_n_m1,X_n,X_n_m1) in enumerate(zip(Y,X[1:],X),1):    Y[IDx] = X_n + X_n_m1 + Y_n_m1

然后有更复杂的选项需要额外的包.最值得注意的是CythonNumba,它们被设计为直接处理数组元素并尽可能避免Python开销.例如,使用Numba,您可以在jitted(即时编译)函数中使用您的方法:

import numba as nb@nb.njitdef func(X,Y):    for n in range(1,len(X)):        Y[n] = X[n] + X[n - 1] + Y[n-1]

X和Y可以是NumPy数组,但numba将直接在缓冲区上工作,超过其他方法(可能是数量级).

Numba比Cython更“重”,但它可以更快更容易使用.但没有conda,很难安装numba … YMMV

然而,这里也是一个Cython版本的代码(使用IPython魔法编译,如果你不使用IPython,它会有点不同):

In [1]: %load_ext cythonIn [2]: %%cython   ...:   ...: cimport cython   ...:   ...: @cython.boundscheck(False)   ...: @cython.wraparound(False)   ...: cpdef cython_indexing(double[:] X,double[:] Y):   ...:     cdef Py_ssize_t n   ...:     for n in range(1,len(X)):   ...:         Y[n] = X[n] + X[n - 1] + Y[n-1]   ...:     return Y

仅举一个例子(基于the timing framework from my answer to another question),关于时间:

import numpy as npimport numba as nbimport scipy.signaldef numpy_indexing(X,len(X)):        Y[n] = X[n] + X[n - 1] + Y[n-1]    return Ydef List_indexing(X,Y):    X = X.toList()    Y = Y.toList()    for n in range(1,len(X)):        Y[n] = X[n] + X[n - 1] + Y[n-1]    return Ydef List_direct(X,Y):    X = X.toList()    Y = Y.toList()    for IDx,1):        Y[IDx] = X_n + X_n_m1 + Y_n_m1    return Y@nb.njitdef numba_indexing(X,len(X)):        Y[n] = X[n] + X[n - 1] + Y[n-1]    return Ydef numpy_cumsum(X,Y):    Y[1:] = X[1:] + X[:-1]    np.cumsum(Y,out=Y)    return Ydef scipy_lfilter(X,Y):    a = [1,-1]    b = [1,1]    return Y[0] - X[0] + scipy.signal.lfilter(b,a,X)# Make sure the approaches give the same resultX = np.random.random(10000)Y = np.zeros(10000)Y[0] = np.random.random()np.testing.assert_array_equal(numba_indexing(X,Y),numpy_indexing(X,Y))np.testing.assert_array_equal(numba_indexing(X,numpy_cumsum(X,Y))np.testing.assert_almost_equal(numba_indexing(X,scipy_lfilter(X,cython_indexing(X,Y))# Timing setuptimings = {numpy_indexing: [],List_indexing: [],List_direct: [],numba_indexing: [],numpy_cumsum: [],scipy_lfilter: [],cython_indexing: []}sizes = [2**i for i in range(1,20,2)]# Timingfor size in sizes:    X = np.random.random(size=size)    Y = np.zeros(size)    Y[0] = np.random.random()    for func in timings:        res = %timeit -o func(X,Y)        timings[func].append(res)# Plottig absolute times%matplotlib notebookimport matplotlib.pyplot as pltfig = plt.figure(1)ax = plt.subplot(111)for func in timings:    ax.plot(sizes,[time.best for time in timings[func]],label=str(func.__name__))ax.set_xscale('log')ax.set_yscale('log')ax.set_xlabel('size')ax.set_ylabel('time [seconds]')ax.grID(which='both')ax.legend()plt.tight_layout()# Plotting relative timesfig = plt.figure(1)ax = plt.subplot(111)baseline = numba_indexing # choose one function as baselinefor func in timings:    ax.plot(sizes,[time.best / ref.best for time,ref in zip(timings[func],timings[baseline])],label=str(func.__name__))ax.set_yscale('log')ax.set_xscale('log')ax.set_xlabel('size')ax.set_ylabel('time relative to "{}"'.format(baseline.__name__))ax.grID(which='both')ax.legend()plt.tight_layout()

结果如下:

绝对运行时

相对运行时间(与numba函数相比)

因此,只需将其转换为列表,您将大约快3倍!通过直接在这些列表上进行迭代,您可以获得另一个(更小的)加速,在此基准测试中只有20%,但与原始解决方案相比,我们现在快了近4倍.使用numba,与列表 *** 作相比,您可以将速度提高100倍以上! Cython只比numba慢一点(约40-50%),可能是因为我没有用Cython来完成所有可能的优化(通常它的速度不会超过10-20%).但是对于大型阵列,差异会变小.

1我在another answer确实进入了更多的细节.那个Q A是关于转换到一个集合但是因为set使用(隐藏)“手动迭代”它也适用于此处.

我包括了NumPy cumsum和Scipy lfilter方法的时间安排.与numba函数相比,对于小阵列,这些大约慢20倍,对于大阵列大约慢4倍.但是,如果我正确地解释了这个问题,那么你不仅要寻找一些方法,而不仅仅是在例子中应并非每个自引用循环都可以使用NumPy或SciPys过滤器的cum *函数来实现.但即便如此,似乎他们无法与Cython和/或numba竞争.

总结

以上是内存溢出为你收集整理的python – 迭代使用自己的输出的数组的最佳方法全部内容,希望文章能够帮你解决python – 迭代使用自己的输出的数组的最佳方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/1195242.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-03
下一篇2022-06-03

发表评论

登录后才能评论

评论列表(0条)

    保存