Domanda

Come posso creare un array numpy di un generatore di oggetto?

Permettetemi di illustrare il problema:

>>> 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 questo caso, gimme() è il generatore la cui uscita mi piacerebbe girare in un array.Tuttavia, la matrice costruttore non iterare il generatore, memorizza semplicemente il generatore stesso.Il comportamento desidero è che da numpy.array(list(gimme())), ma non voglio pagare l'overhead di memoria per avere intermedio elenco e il finale array in memoria contemporaneamente.C'è più spazio-efficiente?

È stato utile?

Soluzione

Array Numpy richiedono la loro lunghezza da impostare in modo esplicito al momento della creazione, a differenza di python liste.Ciò è necessario in modo che lo spazio per ogni elemento può essere consecutivamente allocato in memoria.Consecutivi di allocazione è la caratteristica chiave di numpy matrici:questo, combinato con l'implementazione del codice nativo lasciate che le operazioni su di essi eseguire molto più velocemente rispetto ai normali listini.

Tenendo questo in mente, è tecnicamente impossibile fare un generatore di oggetto e di trasformarlo in un array a meno che non si sia:

  1. in grado di prevedere quanti elementi si produrrà quando esegui:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
    
  2. sono disposti a memorizzare i suoi elementi in un intermedio elenco :

    my_array = numpy.array(list(gimme()))
    
  3. può fare due identici generatori, eseguire attraverso il primo a trovare la lunghezza totale, inizializzare l'array, e quindi eseguire attraverso il generatore di nuovo a trovare ogni elemento:

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

1 è probabilmente quello che stai cercando. 2 è lo spazio inefficiente, e 3 è tempo inefficiente (si deve passare attraverso il generatore di due volte).

Altri suggerimenti

Google dietro questo stackoverflow risultato, ho trovato che c'è un numpy.fromiter(data, dtype, count).L'impostazione predefinita count=-1 prende tutti gli elementi dalla iterable.Si richiede una dtype per essere impostato in modo esplicito.Nel mio caso, questo ha funzionato:

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

Mentre è possibile creare una 1D array da un generatore con numpy.fromiter(), è possibile creare un N-D array da un generatore con numpy.stack:

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

Funziona anche con i 1D array:

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

Nota che numpy.stack internamente consumando il generatore e la creazione di un intermedio elenco arrays = [asanyarray(arr) for arr in arrays].L'implementazione può essere trovato qui.

Un po ' tangenziale, ma se il generatore è riportato un elenco di comprensione, è possibile utilizzare numpy.where è più efficace per ottenere il risultato (l'ho scoperto nel mio codice, dopo aver visto questo post)

Il vstack, hstack, e dstack le funzioni possono prendere come input generatori che producono array multi-dimensionali.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top