Question

Comment puis-je construire un tableau numpy d'un objet générateur?

Permettez-moi d'illustrer le problème:

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

Dans ce cas, donne-moi () est le générateur dont la sortie je voudrais transformer en un tableau. Cependant, le constructeur de tableau n'itérer pas sur le générateur, il stocke simplement le générateur lui-même. Le comportement que je veux est que de numpy.array (liste (Donne-moi ())), mais je ne veux pas payer les frais généraux de mémoire d'avoir la liste intermédiaire et la matrice finale en mémoire en même temps. Y at-il une façon plus efficace l'espace?

Était-ce utile?

La solution

Les tableaux NumPy ont besoin de leur longueur à définir explicitement au moment de la création, à la différence des listes de python. Cela est nécessaire pour que l'espace pour chaque élément peut être attribué consécutivement en mémoire. allocation consécutive est la principale caractéristique de tableaux de numpy:. cela combiné avec la mise en œuvre du code natif laisser les opérations sur les exécuter beaucoup plus rapidement que les listes régulières

En gardant cela à l'esprit, il est techniquement impossible de prendre un objet générateur et la transformer en un tableau à moins que vous soit:

  1. peut prédire le nombre d'éléments qu'il produira lorsqu'il est exécuté:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
    
  2. sont disposés à stocker des éléments dans une liste intermédiaire:

    my_array = numpy.array(list(gimme()))
    
  3. peut faire deux générateurs identiques, de fonctionner à travers le premier à la longueur totale, initialiser le tableau, puis exécuter à nouveau à travers le générateur pour trouver chaque élément:

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

1 est probablement ce que vous recherchez. 2 est un espace inefficace et 3 est temps inefficace (vous devez passer par le générateur deux fois).

Autres conseils

Un google derrière ce résultat stackoverflow, je trouve qu'il ya un numpy.fromiter(data, dtype, count) . La valeur par défaut prend tous les éléments count=-1 du itérables. Il a besoin d'un être réglé dtype explicitement. Dans mon cas, cela a fonctionné:

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

Alors que vous pouvez créer un tableau 1D d'un générateur avec numpy.fromiter(), vous pouvez créer un tableau N-D d'un générateur avec numpy.stack:

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

Il fonctionne également pour les tableaux 1D:

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

Notez que arrays = [asanyarray(arr) for arr in arrays] consomme à l'intérieur du générateur et la création d'une liste intermédiaire avec <=>. La mise en œuvre se trouve .

Un peu tangentielle, mais si votre générateur est une compréhension de la liste, vous pouvez utiliser pour obtenir plus numpy.where efficacement votre résultat (j'ai découvert dans mon propre code après avoir vu ce post)

Le vstack , hstack et dstack fonctions peuvent prendre en tant que générateurs d'entrée que le rendement tableaux multidimensionnels.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top