web-dev-qa-db-fra.com

Comment construire un tableau numpy à partir d'un générateur?

Comment créer un tableau numpy à partir d'un objet générateur?

Me laisser 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, gimme () est le générateur dont je voudrais transformer la sortie en tableau. Cependant, le constructeur de tableau ne parcourt pas le générateur, il stocke simplement le générateur lui-même. Le comportement que je souhaite est celui de numpy.array (list (gimme ())), mais je ne souhaite pas payer le temps système de mémoire associé à la présence simultanée de la liste intermédiaire et du tableau final. Existe-t-il un moyen plus efficace en termes d'espace?

132
saffsd

Les tableaux Numpy nécessitent que leur longueur soit définie explicitement au moment de la création, contrairement aux listes python. Cela est nécessaire pour que de l'espace pour chaque élément puisse être alloué de manière consécutive en mémoire. L'allocation consécutive est la fonctionnalité clé des baies numpy: ceci, combiné à une implémentation de code natif, permet aux opérations sur celles-ci de s'exécuter beaucoup plus rapidement que les listes normales.

Gardant cela à l'esprit, il est techniquement impossible de transformer un objet générateur en tableau, à moins de:

  1. peut prédire combien d’éléments il produira lorsqu’il sera exécuté:

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

    my_array = numpy.array(list(gimme()))
    
  3. peut créer deux générateurs identiques, parcourir le premier pour trouver la longueur totale, initialiser le tableau, puis réexécuter le générateur pour rechercher 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 cherchez. 2 l'espace est inefficace et 3 le temps est inefficace (vous devez passer par le générateur deux fois).

112
shsmurfy

Un google derrière ce résultat de stackoverflow, j'ai trouvé qu'il y a un numpy.fromiter(data, dtype, count) . La valeur par défaut count=-1 prend tous les éléments de l'itérable. Il faut que dtype soit défini explicitement. Dans mon cas, cela a fonctionné:

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

186
dhill

Bien que vous puissiez créer un tableau 1D à partir d'un générateur avec numpy.fromiter(), vous pouvez créer un tableau N-D à partir 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)

Cela fonctionne aussi 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 numpy.stack consomme en interne le générateur et crée une liste intermédiaire avec arrays = [asanyarray(arr) for arr in arrays]. La mise en œuvre peut être trouvée ici .

9
mdeff

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

5

Les fonctions vstack , hstack , et dstack peuvent prendre en tant que générateurs d’entrée qui produisent des tableaux multidimensionnels.

0
msftmike