4

のこの奇妙な動作に出くわしました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ドキュメントによると、結果はiterableと同じになるはずですが、なぜ最初のケースで a のnumpy.float64代わりに a が返されるのnumpy.uint64ですか? そして、最後の例はどのような種類の合計も返さず、エラーも発生しないのはなぜですか?

4

2 に答える 2

6

一般に、numpy 関数は、ジェネレーターを操作するときに期待どおりに動作するとは限りません。numpy 配列を作成するには、作成する前にそのサイズと型を知る必要がありますが、これはジェネレータでは不可能です。非常に多くの numpy 関数は、ジェネレーターでは機能しないか、Python ビルトインにフォールバックするこの種のことを行います。

ただし、同じ理由で、Numpy コンテキストではジェネレーターの使用はあまり役に立ちません。Numpy オブジェクトからジェネレーターを作成しても、Numpy オブジェクト全体をメモリに格納する必要があるため、実際の利点はありません。指定したとおりにすべてのタイプを維持する必要がある場合は、Numpy オブジェクトをジェネレーターでラップしないでください。

詳細情報: 技術的には、 への引数np.sumは反復可能なオブジェクトではなく、「配列のような」オブジェクトであると想定されています。配列のようなものは、ドキュメントで次のように定義されています。

配列、配列インターフェイスを公開する任意のオブジェクト、__array__メソッドが配列を返すオブジェクト、または任意の (ネストされた) シーケンス。

配列インターフェースについては、ここに文書化されています。基本的に、配列は固定された形状と均一な型を持つ必要があります。

ジェネレーターはこのプロトコルに適合しないため、実際にはサポートされていません。多くの numpy 関数は素晴らしく、技術的に配列のようなものとして認定されない他の種類のオブジェクトを受け入れますが、ドキュメントを厳密に読むと、この動作に依存できないことがわかります。操作は機能する可能性がありますが、すべての型が完全に保持されるとは期待できません。

于 2012-12-15T08:21:52.620 に答える
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

と に適用しようとするsumgenlst結果が同じであることがわかります6.0

の 2 番目のパラメーターはsumstart、デフォルトは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

于 2012-12-15T08:13:18.217 に答える