ジェネレーターから派手な配列を構築するにはどうすればよいですか?


166

ジェネレータオブジェクトから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()))ですが、中間リストと最終配列を同時にメモリに持つことによるメモリのオーバーヘッドを負担したくありません。よりスペース効率の良い方法はありますか?


6
これは興味深い問題です。私はこれに遭遇しましたfrom numpy import *; print any(False for i in range(1))-これは組み込みをシャドウany()し、反対の結果を生成します(私が知っているように)。
moooeeeep

4
@moooeeeepひどいです。もしnumpyPythonはないとして、それは引数として発電機を受信したときにできるわけではありません(あるいはしたくない)治療ジェネレータに、少なくとも、それは例外を発生させなければなりません。
最大

1
@maxまったく同じものを踏んだ。どうやらこれはNumPyリスト(およびそれ以前)で発生したものであり、例外を発生させるために変更されないため、常に名前空間を使用する必要があると結論付けています。
アレクセイ2014年

回答:


128

Numpy配列は、Pythonリストとは異なり、作成時に明示的に長さを設定する必要があります。これは、各アイテムのスペースをメモリに連続して割り当てるために必要です。連続した割り当ては、numpy配列の重要な機能です。これは、ネイティブコードの実装と組み合わせると、通常のリストよりもはるかに高速にそれらの操作を実行できます。

これを念頭に置いて、次のいずれかを行わない限り、ジェネレーターオブジェクトを取得して配列に変換することは技術的に不可能です。

  1. 実行時に生成される要素の数を予測できます。

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
    
  2. その要素を中間リストに保存する用意があります:

    my_array = numpy.array(list(gimme()))
  3. 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はスペース効率が悪く、3は時間効率が悪いです(ジェネレーターを2回通過する必要があります)。


11
組み込みarray.arrayはリンクされていない連続したリストであり、簡単にできますarray.array('f', generator)。不可能だと言うのは誤解を招くものです。それは単なる動的な割り当てです。
Cuadue 2013

1
Cuadueが言うように、なぜnumpy.arrayは組み込みのarray.arrayと同じ方法でメモリ割り当てを行わないのですか。トレードオフとは何ですか?両方の例に連続して割り当てられたメモリがあるので、私は尋ねます。か否か?
jgomo3 2013

3
numpyは、配列サイズが変化しないことを前提としています。同じメモリチャンクのさまざまなビューに大きく依存しているため、たとえば、配列を拡張して再割り当てできるようにするには、ビューを有効にするために間接層を追加する必要があります。
joeln 2013

2
emptyを使用すると、少し速くなります。何らかの方法で値を初期化するので、これを2回行う必要はありません。
Kaushik Ghose 2015年

参照してください@ dhillの答え、それ以下がより速くより1です
ビル・

206

このstackoverflowの結果の背後にあるグーグル、私はあることを発見しましたnumpy.fromiter(data, dtype, count)。デフォルトでcount=-1は、すべての要素が反復可能オブジェクトから取得されます。をdtype明示的に設定する必要があります。私の場合、これはうまくいきました:

numpy.fromiter(something.generate(from_this_input), float)


これを質問にどのように適用しますか?numpy.fromiter(gimme(), float, count=-1)動作しません。何のsomething略ですか?
マティアス009

1
@ Matthias009 numpy.fromiter(gimme(), float, count=-1)は私のために働きます。
moooeeeep

14
fromiter1Dアレイでのみ機能する理由を説明するスレッド:mail.scipy.org/pipermail/numpy-discussion/2007-August/…
最大

2
fwiw count=-1はデフォルトなので、指定する必要はありません。
askewchan 2013年

5
イテラブルの長さが事前にわかっている場合は、countパフォーマンスを向上させるためにを指定します。このように、オンデマンドでサイズ変更するのではなく、値で埋める前にメモリを割り当てます(のドキュメントを参照numpy.fromiter
Eddy

15

を使用してジェネレータから1D配列をnumpy.fromiter()作成できますが、次を使用してジェネレータからND配列を作成できますnumpy.stack

>>> 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]。実装はここにあります


1
これは指摘してくれてありがとう、きちんとした解決策です。しかし、(私のアプリケーションでは)を使用するよりもかなり遅いようnp.array(tuple(mygen))です。テスト結果は次のとおりです。%timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loop比較%timeit np.array(tuple(permutations(range(10), 7))) 1 loop, best of 3: 427 ms per loop
Bill

13
これは素晴らしいようで、私にとってはうまくいきます。しかし、numpyの1.16.1と私はこの警告を得る:FutureWarning: arrays to stack must be passed as a "sequence" type such as list or tuple. Support for non-sequence iterables such as generators is deprecated as of NumPy 1.16 and will raise an error in the future.
ジョセフ・シーディ

6

多少正接しますが、ジェネレータがリスト内包である場合numpy.where、より効果的に結果を取得するために使用できます(この投稿を見て、自分のコードでこれを発見しました)


0

vstackhstack、及びdstackの機能は、多次元配列を生じる入力ジェネレータとして取ることができます。


3
リンクが変わった場合の例を挙げていただけますか?:)
Ari Cooper-Davis、

これらの関数は、値のジェネレーターではなく配列のジェネレーターを取ることができます
retnikt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.