如何在numpy中加速循环?

时间:2015-07-25 04:06:38

标签: python numpy

我想加快这段代码:

import numpy as np
import pandas as pd

a = pd.read_csv(path)
closep = a['Clsprc']
delta = np.array(closep.diff())
upgain = np.where(delta >= 0, delta, 0)
downloss = np.where(delta <= 0, -delta, 0)
up = sum(upgain[0:14]) / 14
down = sum(downloss[0:14]) / 14
u = []
d = []
for x in np.nditer(upgain[14:]):
    u1 = 13 * up + x
    u.append(u1)
    up = u1
for y in np.nditer(downloss[14:]):
    d1 = 13 * down + y
    d.append(d1)
    down = d1

以下数据:

0     49.00
1     48.76
2     48.52
3     48.28
...
36785758    13.88
36785759    14.65
36785760    13.19

Name: Clsprc, Length: 36785759, dtype: float64

for循环太慢了,我该怎么做才能加快这段代码的速度?我可以对整个操作进行矢量化吗?

2 个答案:

答案 0 :(得分:0)

看起来你正试图计算一个指数移动平均线(滚动平均值),但忘记了除法。如果是这种情况,那么您可能希望看到this SO问题。同时,这是一个快速的简单移动平均线,使用从引用链接获取的cumsum()函数。

def moving_average(a, n=14) :
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n

如果不是这种情况,并且您确实需要所描述的功能,则可以通过在迭代中使用external_loop标志来提高迭代速度。从numpy文档:

  

nditer将尝试提供尽可能大的块   内循环。通过强制'C'和'F'顺序,我们会有所不同   外部循环大小。通过指定迭代器来启用此模式   标志。

     

观察默认情况下保持本机内存顺序,   迭代器能够提供单个一维块,而   当强制Fortran命令时,它必须提供三个两个块   每个元素。

for x in np.nditer(upgain[14:],  flags=['external_loop'], order='F'):
    # x now has x[0],x[1], x[2], x[3], x[4], x[5] elements.

答案 1 :(得分:0)

简单来说,我认为这就是循环正在做的事情:

upgain=np.array([.1,.2,.3,.4])    
u=[]
up=1
for x in upgain:                  
    u1=10*up+x
    u.append(u1)
    up=u1
制造

[10.1, 101.2, 1012.3, 10123.4]

np.cumprod([10,10,10,10])已经存在,加上cumsum条款的修改后的[.1,.2,.3,.4]。但我不能想到将这些与编译的numpy函数结合起来的方法。我们可以编写自定义ufunc,并使用其accumulate。或者我们可以在cython(或其他c接口)中编写它。

https://stackoverflow.com/a/27912352表明frompyfunc是撰写广义accumulate的一种方式。我不希望节省大量时间,也许是2倍。

要使用frompyfunc,请定义:

def foo(x,y):return 10*x+y

循环应用程序(上面)将是

def loopfoo(upgain,u,u1):
    for x in upgain:
        u1=foo(u1,x)
        u.append(u1)
    return u

'矢量化'版本将是:

vfoo=np.frompyfunc(foo,2,1) # 2 in arg, 1 out
vfoo.accumulate(upgain,dtype=object).astype(float)

先前的SO和https://github.com/numpy/numpy/issues/4155

中注明了dtype=object要求
In [1195]: loopfoo([1,.1,.2,.3,.4],[],0)
Out[1195]: [1, 10.1, 101.2, 1012.3, 10123.4]

In [1196]: vfoo.accumulate([1,.1,.2,.3,.4],dtype=object)
Out[1196]: array([1.0, 10.1, 101.2, 1012.3, 10123.4], dtype=object)

对于这个小清单,loopfoo更快(3μsv21μs)

对于100个元素的数组,例如biggain=np.linspace(.1,1,100)vfoo.accumulate更快:

In [1199]: timeit loopfoo(biggain,[],0)
1000 loops, best of 3: 281 µs per loop

In [1200]: timeit vfoo.accumulate(biggain,dtype=object)
10000 loops, best of 3: 57.4 µs per loop

对于更大的biggain=np.linspace(.001,.01,1000)(更小的数字以避免溢出),5倍速比仍然存在。