对range和range_iterator对象的numpy.sum

时间:2016-07-09 05:59:26

标签: python python-3.x numpy ipython

在python 3下考虑Ipython上的这个性能测试:

创建范围,range_iterator和生成器

In [1]: g1 = range(1000000)

In [2]: g2 = iter(range(1000000))

In [3]: g3 = (i for i in range(1000000))

使用python原生和来衡量求和的时间

In [4]: %timeit sum(g1)
10 loops, best of 3: 47.4 ms per loop

In [5]: %timeit sum(g2)
The slowest run took 374430.34 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 123 ns per loop

In [6]: %timeit sum(g3)
The slowest run took 1302907.54 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 128 ns per loop

不确定是否应该担心警告。范围版本的时间变化很长(为什么?),但range_iterator和生成器是相似的。

现在让我们使用numpy.sum

In [7]: import numpy as np

In [8]: %timeit np.sum(g1)
10 loops, best of 3: 174 ms per loop

In [9]: %timeit np.sum(g2)
The slowest run took 8.47 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 6.51 µs per loop

In [10]: %timeit np.sum(g3)
The slowest run took 9.59 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 446 ns per loop

g1和g3变得慢了~3.5,但是range_iterator g2现在比原始总和慢约50倍。 g3获胜。

In [11]: type(g1)
Out[11]: range

In [12]: type(g2)
Out[12]: range_iterator

In [13]: type(g3)
Out[13]: generator

为什么对numpy.sum上的range_iterator这样的惩罚?是否应避免此类物品?它是否概括 - 做"自制"发电机总是击败numpy上的其他物体?

编辑1:我意识到np.sum不会评估range_iterator但会返回另一个range_iterator对象。所以这种比较并不好。为什么不对它进行评估?

编辑2:我也意识到numpy.sum会以整数形式保留范围,因此会因整数溢出而在我的总和上给出错误的结果。

In [12]: sum(range(1000000))
Out[12]: 499999500000

In [13]: np.sum(range(1000000))
Out[13]: 1783293664

In [14]: np.sum(range(1000000), dtype=float)
Out[14]: 499999500000.0

中间结论 - 不要在非numpy对象上使用numpy.sum ......?

1 个答案:

答案 0 :(得分:3)

你看过iter上重复总和的结果吗?

95:~/mypy$ g2=iter(range(10))
96:~/mypy$ sum(g2)
Out[96]: 45
97:~/mypy$ sum(g2)
Out[97]: 0
98:~/mypy$ sum(g2)
Out[98]: 0

为什么是0?因为g2只能使用一次。同样适用于生成器表达式。

或者用列表

查看
100:~/mypy$ g2=iter(range(10))
101:~/mypy$ list(g2)
Out[101]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
102:~/mypy$ list(g2)
Out[102]: []

在Python 3中,range是范围对象,而不是列表。所以它是一个迭代器,每次使用时都会重新生成。

至于np.sumnp.sum(range(10))必须先制作数组。

在列表上操作时,Python sum非常快,比np.sum更快:

116:~/mypy$ %%timeit x=list(range(10000))
       ...: sum(x)
1000 loops, best of 3: 202 µs per loop

117:~/mypy$ %%timeit x=list(range(10000))
       ...: np.sum(x)
1000 loops, best of 3: 1.62 ms per loop

但是在数组上运行,np.sum做得更好

118:~/mypy$ %%timeit x=np.arange(10000)
       ...: sum(x)
100 loops, best of 3: 5.92 ms per loop

119:~/mypy$ %%timeit x=np.arange(10000)
       ...: np.sum(x)
<caching warning>
100000 loops, best of 3: 18.6 µs per loop

另一个时间 - 制作阵列的各种方法。 fromiter可能比np.array更快;但内置arange要好得多。

124:~/mypy$ timeit np.array(range(100000))
10 loops, best of 3: 39.2 ms per loop
125:~/mypy$ timeit np.fromiter(range(100000),int)
100 loops, best of 3: 12.9 ms per loop
126:~/mypy$ timeit np.arange(100000)
The slowest run took 6.93 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 106 µs per loop

如果您打算使用列表,请使用range;但如果需要使用数组,请使用numpy's自己的范围。创建数组时会产生开销,因此在处理大数组时它们更有价值。

==================

关于np.sum如何处理迭代器的问题 - 它没有。看看np.array对这样一个对象的作用:

In [12]: np.array(iter(range(10)))
Out[12]: array(<range_iterator object at 0xb5998f98>, dtype=object)

它生成一个带有dtype对象的单个元素数组。

fromiter将评估此迭代:

In [13]: np.fromiter(iter(range(10)),int)
Out[13]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
在将输入转换为数组时,

np.array遵循一些复杂的规则。它主要用于工作,包括数字列表或嵌套的等长列表。

如果您对np函数如何处理非数组对象有疑问,请首先检查np.array对该对象的作用。