ジェネレーターオブジェクトからnumpy配列を構築するにはどうすればよいですか?
問題を説明しましょう:
>>> import numpy
>>> def gimme():
... for x in xrange(10):
... yield x
...
>>> gimme()
<generator object at 0x28a1758>
>>> list(gimme())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numpy.array(xrange(10))
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> numpy.array(gimme())
array(<generator object at 0x28a1758>, dtype=object)
>>> numpy.array(list(gimme()))
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
この例では、gimme()は、出力を配列に変換するジェネレーターです。ただし、配列コンストラクターはジェネレーターを反復処理せず、単にジェネレーター自体を格納します。私が望む動作はnumpy.array(list(gimme()))からのものですが、メモリ内に中間リストと最終配列を同時に持つことのメモリオーバーヘッドを支払いたくありません。よりスペース効率の良い方法はありますか?
Numpy配列では、pythonリストとは異なり、作成時に明示的に長さを設定する必要があります。これは、各アイテムのスペースをメモリに連続して割り当てるために必要です。配列:ネイティブコードの実装と組み合わせることで、それらに対する操作を通常のリストよりもはるかに迅速に実行できます。
これを念頭に置いて、以下のいずれかの場合を除き、ジェネレーターオブジェクトを取得して配列に変換することは技術的に不可能です。
実行時に生成される要素の数を予測できます。
my_array = numpy.empty(predict_length())
for i, el in enumerate(gimme()): my_array[i] = el
その要素を中間リストに保存することをいとわない:
my_array = numpy.array(list(gimme()))
2つの同一のジェネレーターを作成し、最初のジェネレーターを実行して全長を見つけ、配列を初期化してから、再度ジェネレーターを実行して各要素を見つけます。
length = sum(1 for el in gimme())
my_array = numpy.empty(length)
for i, el in enumerate(gimme()): my_array[i] = el
1はおそらくあなたが探しているものです。 2はスペース効率が悪く、は時間効率が悪い(ジェネレーターを2回通過する必要がある)。
このstackoverflowの結果の背後にあるグーグル、私は numpy.fromiter(data, dtype, count)
があることがわかりました。デフォルトの_count=-1
_は、イテラブルからすべての要素を取ります。 dtype
を明示的に設定する必要があります。私の場合、これはうまくいきました:
numpy.fromiter(something.generate(from_this_input), float)
numpy.fromiter()
を使用してジェネレーターから1D配列を作成できますが、_numpy.stack
_を使用してジェネレーターからN-D配列を作成できます。
_>>> mygen = (np.ones((5, 3)) for _ in range(10))
>>> x = numpy.stack(mygen)
>>> x.shape
(10, 5, 3)
_
1D配列でも機能します。
_>>> numpy.stack(2*i for i in range(10))
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
_
_numpy.stack
_はジェネレーターを内部的に消費し、arrays = [asanyarray(arr) for arr in arrays]
で中間リストを作成していることに注意してください。実装は here にあります。
やや接線方向ですが、ジェネレーターがリスト内包である場合は、numpy.where
より効果的に結果を得るために(この投稿を見た後、自分のコードでこれを発見しました)