Python代码循环速度比较

时间:2013-10-07 19:26:50

标签: python numpy

为什么这行Python

yy = [sum(y[i:i+5])/5. for i in range(len(y)-4)]

运行速度比以下(等效)代码快20倍?

for i in xrange(0,len(y)-4):    
    yy = np.append(yy, sum(y[i:i+5])/5.) 

其中y是大量的实数。 引擎盖下到底发生了什么? 非常感谢。

2 个答案:

答案 0 :(得分:3)

两个代码等效。正确的等效版本是:

yy = []
for i in range(0,len(y)-4):    
    yy.append(sum(y[i:i+5])/5.)

大概需要相同的时间:

In [10]: y = [1.0] * 100000

In [11]: %timeit [sum(y[i:i+5])/5. for i in range(len(y)-4)]
10 loops, best of 3: 49.6 ms per loop

In [12]: %%timeit yy = []
    ...: for i in range(0,len(y)-4):    
    ...:     yy.append(sum(y[i:i+5])/5.)
    ...: 
10 loops, best of 3: 55.1 ms per loop

问题是对numpy.append的调用比list.append慢得多。 这可能是因为numpy.append正在创建数组的副本并为每次插入返回。 第一次插入成本2(为1个元素分配空间并将其复制到那里)。秒成本3(为2个元素分配空间,复制单个元素和新元素)。第三个成本4(分配3个,复制2个元素和新元素)。等

这意味着算法突然变为O(n^2),而O(n)使用python list s ,因为它们复制每个append的整个列表。它们非常聪明,可以分配更多内存以容纳更多元素。

此外,作为一般规则,numpy对单元素访问执行闪耀。在这种情况下,它实际上比纯python 更慢,因为它必须始终在机器数据类型和python对象之间进行转换。尝试对操作进行矢量化,你会发现速度很快。

答案 1 :(得分:3)

numpy旨在执行向量化操作:如果你必须继续调用numpy.append,每次调用的开销将使它不值得。

numpy中执行此操作(滚动方式)的正确方法是对其进行矢量化,例如使用convolve函数(感谢@askewchan的建议)。在这种情况下,比列表理解更快

import timeit
import numpy as np

y = np.random.normal(0, 1, 10000)

print timeit.timeit("np.convolve(y, np.ones(5)/5, mode='valid')",
                    setup = "from __main__ import y; import numpy as np",
                    number=100) 

print timeit.timeit("[sum(y[i:i+5])/5. for i in range(len(y)-4)]",
                    setup = "from __main__ import y",
                    number=100)

在我的机器上,100次迭代的numpy矢量化解决方案需要0.03秒,而列表理解需要6.56秒。