内置的 range 函数和 numpy 中的 arange 函数,哪个更高效?

82

当使用范围表达式迭代大型数组时,我应该使用Python内置的range函数还是numpy的arange以获得最佳性能?

我的推理如下:

range可能采用本地实现,因此可能更快。另一方面,arange返回完整的数组,占用内存,因此可能存在开销。 Python 3的range表达式是一个生成器,不会在内存中保存所有值。


1
arange 可能是打错了,应该是 range。 - Chan Kim
3个回答

92
对于大数组,向量化的numpy操作是最快的。如果必须循环,请使用xrange/range,并避免使用np.arange
在numpy中,应使用向量化计算、ufuncs索引的组合来解决问题,因为它以C速度运行。 与此相比,循环numpy数组效率低下。
(就像你在问题的第一句话中提到的那样,使用rangenp.arange创建的索引迭代数组是最糟糕的做法之一,但我不确定你是否真的是这个意思。)
import numpy as np
import sys

sys.version
# out: '2.7.3rc2 (default, Mar 22 2012, 04:35:15) \n[GCC 4.6.3]'
np.version.version
# out: '1.6.2'

size = int(1E6)

%timeit for x in range(size): x ** 2
# out: 10 loops, best of 3: 136 ms per loop

%timeit for x in xrange(size): x ** 2
# out: 10 loops, best of 3: 88.9 ms per loop

# avoid this
%timeit for x in np.arange(size): x ** 2
#out: 1 loops, best of 3: 1.16 s per loop

# use this
%timeit np.arange(size) ** 2
#out: 100 loops, best of 3: 19.5 ms per loop

所以在这种情况下,如果使用正确的方法,numpy比使用xrange快4倍。根据您的问题,numpy可以比4或5倍速度更快。

这个问题的答案解释了使用numpy数组而不是Python列表处理大型数据集的一些其他优点。


1
值得一提的是,在Python3.x中没有xrange,而range与其相同。 - Benyamin Jafari
另外,我测试了numpy==1.16.4python 3.6中的for x in np.arange(size): x ** 2,比for x in range(size): x ** 2更快。 - Benyamin Jafari

13

首先,正如@bmu所提到的,你应该使用向量化计算、ufuncs和索引的组合。确实有一些情况需要明确循环,但这些情况真的很少见。

如果需要明确循环,在Python 2.6和2.7中,你应该使用xrange(见下文)。根据你所说,在Python 3中,rangexrange相同(返回生成器)。所以,也许range对你来说也很好用。

现在,你应该亲自尝试一下 (使用timeit:- 在这里使用ipython "magic function"):

%timeit for i in range(1000000): pass
[out] 10 loops, best of 3: 63.6 ms per loop

%timeit for i in np.arange(1000000): pass
[out] 10 loops, best of 3: 158 ms per loop

%timeit for i in xrange(1000000): pass
[out] 10 loops, best of 3: 23.4 ms per loop

如上所述,通常情况下可以使用numpy向量/数组公式(或ufunc等...),其运行速度快得多,这就是我们所说的“向量编程”。与C相比,它使程序更易于实现(并且更易读),但最终速度几乎一样。


3
有一个标准的Python 'timeit' 模块,可以在不使用IPython的情况下完成相同的操作。但是使用这个魔法函数要简单得多。 - Juh_
我完全同意,但问题是关于“迭代大数组”的。在某些情况下,无法使用numpy向量化、ufunc或索引。我遇到的一个例子是计算一组矩阵(>= 3x3)的特征向量。 - Juh_
2
我不知道自2012年以来np.arange是否已经变得更有效率,但与内置的range相比,它的主要优势在于可以使用浮点数作为起始点:终止点:步长。 - Guimoute
1
np.arange 在我们需要使用非整数步长时非常有用。 - Maulik Madhavi

1
首先:`range` 返回一个迭代器,`np.arange` 返回一个已分配内存的 `np.array`(正如 OP 已经提到的)。为了使这两种方法可比较,我们需要为迭代器分配一个列表:
list(range(n))

说到性能,这取决于:对于较小(已分配)的范围,Python 的range(...)更快。但是,numpynp.arange(...)可以很好地扩展,并在较大的范围内表现更好:

enter image description here

请在此处找到基准代码 here。在 MacBook Pro M1 上使用 Python 3.11 和 numpy 1.23.5 运行。
这个问题非常关注性能方面(完全合理)。然而,我认为可用性/可维护性的观点同样重要:仅为创建范围导入numpy将是可怕的。反之亦然:一个严重依赖于 numpy 的代码使用 range 感觉不对。
另外,请记住,np.arange 对于非整数步长有更好的支持。
总之:这取决于情况。 :-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接