我刚刚遇到numpy.sum
的这种奇怪行为:
>>> import numpy
>>> ar = numpy.array([1,2,3], dtype=numpy.uint64)
>>> gen = (el for el in ar)
>>> lst = [el for el in ar]
>>> numpy.sum(gen)
6.0
>>> numpy.sum(lst)
6
>>> numpy.sum(iter(lst))
<listiterator object at 0x87d02cc>
根据文档,结果应与可迭代的dtype
相同,但为什么在第一种情况下返回numpy.float64
而不是numpy.uint64
?
为什么最后一个例子不会返回任何类型的总和,也不会引起任何错误?
答案 0 :(得分:6)
通常,在使用生成器时,numpy函数并不总是能达到预期效果。要创建numpy数组,您需要知道它的大小并在创建它之前键入,这对于生成器是不可能的。如此多的numpy函数要么不能与生成器一起使用,要么在它们回退到Python内置函数时做这种事情。
但是,出于同样的原因,使用生成器通常在Numpy上下文中没用。从Numpy对象生成生成器没有什么好处,因为无论如何你必须在内存中拥有整个Numpy对象。如果您需要保留所有类型,则不应将Numpy对象包装在生成器中。
更多信息:从技术上讲,np.sum
的参数应该是一个“类似数组”的对象,而不是可迭代的。类似数组的定义为in the documentation:
数组,公开数组接口的任何对象,
__array__
方法返回数组的对象,或任何(嵌套)序列。
数组接口记录为here。基本上,阵列必须具有固定的形状和统一的类型。
生成器不适合此协议,因此不受支持。许多numpy函数都很好,并且会接受其他类型的对象,这些对象在技术上不符合类似数据的要求,但严格阅读文档意味着你不能依赖这种行为。这些操作可能有效,但你不能指望所有类型都能完美保存。
答案 1 :(得分:5)
如果参数是生成器,则使用Python的内置sum
。
您可以在numpy.sum
(numpy / core / fromnumeric.py)的源代码中看到这一点:
0 if isinstance(a, _gentype):
1 res = _sum_(a)
2 if out is not None:
3 out[...] = res
4 return out
5 return res
_gentype
只是types.GeneratorType
的别名,_sum_
是内置sum
的别名。
如果您尝试将sum
应用于gen
和lst
,您可以看到结果相同:6.0
。
sum
的第二个参数是start
,默认为 0 ,这是使您的结果为float64
的部分原因。
In [1]: import numpy as np
In [2]: type(np.uint64(1) + np.uint64(2))
Out[2]: numpy.uint64
In [3]: type(np.uint64(1) + 0)
Out[3]: numpy.float64
修改强>:
顺便说一句,我在这个问题上找到了一张票,标注为wontfix
:http://projects.scipy.org/numpy/ticket/669