【问题标题】:numpy.sum on range and range_iterator objectsrange 和 range_iterator 对象上的 numpy.sum
【发布时间】:2016-11-11 17:51:41
【问题描述】:

考虑在 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 原生 sum 测量求和时间

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 版本时序变化很大(为什么?),但是 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 变慢了 x~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...?

【问题讨论】:

  • 这是在重申之前被标记为离题的类似问题。希望你能找到这个主题。
  • @SSDMS - 答案是给import array,而不是import numpy
  • 由于你使用的是Ipython,所以使用np.sum??查看它的代码。除了generator(不是迭代器)的特殊情况,np.sum 对输入的np.array 版本进行操作。因此,如果np.sum 的行为让您感到困惑,请查看np.array(arg)

标签: python python-3.x numpy ipython


【解决方案1】:

你看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 只能使用一次。生成器表达式也是如此。

或者用list看看

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 对该对象的作用。

【讨论】:

    猜你喜欢
    • 2012-10-28
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 2018-09-21
    • 1970-01-01
    • 2017-01-06
    • 2022-06-13
    • 1970-01-01
    相关资源
    最近更新 更多