Frage

Wie kann ich eine numpy Array aus einem Generator Objekt bauen?

Lassen Sie mich das Problem veranschaulichen:

>>> 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])

In diesem Fall Gib mir () ist der Generator, dessen Ausgang ich möchte in ein Array drehen. Allerdings ist der Array-Konstruktor den Generator nicht iterieren, es speichert einfach den Generator selbst. Das Verhalten, das ich wünsche, ist, dass aus numpy.array (Liste (Gimme ())), aber ich habe nicht den Speicher-Overhead bezahlt die Zwischen Liste mit und der endgültige Anordnung im Speicher zur gleichen Zeit. Gibt es eine platzsparende Art und Weise?

War es hilfreich?

Lösung

Numpy Arrays benötigen, um ihre Länge explizit bei der Erstellung festgelegt werden, im Gegensatz zu Python-Listen. Dies ist notwendig, so dass Raum für jedes Element kann nacheinander im Speicher zugeordnet werden. Aufeinander folgende Zuordnung ist das Hauptmerkmal von numpy Arrays. Dies mit nativen Code-Implementierung kombiniert lassen Operationen auf sie ausführen viel schneller als normale Listen

Vor diesem Hintergrund ist es technisch unmöglich, einen Generator Objekt zu nehmen und es in ein Array, wenn Sie entweder:

  1. kann vorhersagen, wie viele Elemente es wird ergeben, wenn sie ausgeführt werden:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
    
  2. bereit sind, ihre Elemente in einer Zwischenliste zu speichern:

    my_array = numpy.array(list(gimme()))
    
  3. kann zwei identische Generatoren zu bilden, durch die ersten ausführen, um die Gesamtlänge zu finden, um das Array initialisieren, und dann durch den Generator wieder läuft um jedes Element zu finden:

    length = sum(1 for el in gimme())
    my_array = numpy.empty(length)
    for i, el in enumerate(gimme()): my_array[i] = el
    

1 ist wahrscheinlich das, was Sie suchen. 2 ist Raum ineffizient und 3 ist Zeit ineffizient (Sie müssen zweimal durch den Generator gehen).

Andere Tipps

Eine google hinter diesem Ergebnis Stackoverflow, fand ich, dass es ein numpy.fromiter(data, dtype, count) . Der Standard count=-1 nimmt alle Elemente aus der iterable. Es erfordert eine dtype explizit festgelegt werden. In meinem Fall, dies funktioniert:

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

Während Sie ein 1D-Array von einem Generator mit numpy.fromiter() erstellen können, können Sie ein N-D-Array von einem Generator mit numpy.stack erstellen:

>>> mygen = (np.ones((5, 3)) for _ in range(10))
>>> x = numpy.stack(mygen)
>>> x.shape
(10, 5, 3)

Es funktioniert auch für 1D-Arrays:

>>> numpy.stack(2*i for i in range(10))
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

Beachten Sie, dass numpy.stack intern den Generator raubend und eine Zwischen Liste mit arrays = [asanyarray(arr) for arr in arrays] zu schaffen. Die Implementierung kann hier .

Etwas tangential, aber wenn Ihr Generator eine Liste Verständnis ist, können Sie numpy.where effektiver Ihr Ergebnis erhalten verwenden (Ich entdecken dies in meinem eigenen Code nach diesem Beitrag zu sehen)

Die vstack , hstack und dstack Funktionen als Eingabegeneratoren nehmen können, die Ausbeute multidimensionalen Arrays.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top