【问题标题】:how to speed up loop in numpy?如何加快numpy中的循环?
【发布时间】:2015-07-25 04:06:37
【问题描述】:

我想加快这段代码的速度:

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 循环太慢了,我该怎么做才能加快这段代码的速度?我可以向量化整个操作吗?

【问题讨论】:

  • a['Clsprc'] 的大小是多少?
  • 以编程方式我会寻找一种方法来并行化操作,例如 NumPy 数组或其他集合数据类型的并行实现(例如可用于 Scala 集合)。有许多方法可以并行化 for 循环,例如参见 pythonhosted.org/joblib/parallel.html。 Anaconda 有一个 MKL 优化包,专门用于提高 NumPy、SciPy、scikit-learn 和 NumExpr 的性能,请参阅store.continuum.io/cshop/mkl-optimizations,但它不是免费的。
  • 那真的是正确的代码吗?这些乘以 13 的乘法对我来说看起来很奇怪——你的值很快就会消失。我认为您正在尝试计算滚动平均值并且忘记了除法,但是由于您的代码没有记录,因此您没有解释您要做什么,而且您的实现似乎很奇怪,很难确定。

标签: python numpy


【解决方案1】:

您似乎正在尝试计算指数移动平均值(滚动平均值),但忘记了除法。如果是这种情况,那么您可能希望看到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.

【讨论】:

  • 对于一维数组,外部循环迭代器只需一步分发整个数组。与x=upgain[...]相同。
  • “尽可能大”是如何确定的?这可以用于通过将 mmap 数组分解成适合内存的块来处理它吗?
【解决方案2】:

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

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)

dtype=object 要求已在之前的 SO 中注明,https://github.com/numpy/numpy/issues/4155

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µs v 21µ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 倍速比保持不变。

【讨论】:

    猜你喜欢
    • 2014-07-27
    • 2016-10-14
    • 2019-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 1970-01-01
    相关资源
    最近更新 更多