Comment puis-je créer un tableau numpy à partir d'un objet générateur?
Laissez-moi 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 cet exemple, gimme()
est le générateur dont je voudrais transformer la sortie en un tableau. Cependant, le constructeur de tableau n'itère pas sur le générateur, il stocke simplement le générateur lui-même. Le comportement que je désire est celui de numpy.array(list(gimme()))
, mais je ne veux pas payer la surcharge de mémoire d'avoir la liste intermédiaire et le tableau final en mémoire en même temps. Existe-t-il un moyen plus efficace d'espace?
from numpy import *; print any(False for i in range(1))
- qui ombres le intégréany()
et produit le résultat opposé (comme je le sais maintenant).numpy
ne peut pas (ou ne veut pas) traiter les générateurs comme le fait Python, au moins il devrait lever une exception lorsqu'il reçoit un générateur comme argument.Réponses:
Les tableaux Numpy nécessitent que leur longueur soit définie explicitement au moment de la création, contrairement aux listes python. Ceci est nécessaire pour que l'espace pour chaque élément puisse être alloué consécutivement en mémoire. L'allocation consécutive est la caractéristique clé des tableaux numpy: cela, combiné à l'implémentation de code natif, permet aux opérations sur eux de s'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 de le transformer en un tableau sauf si vous:
peut prédire le nombre d'éléments qu'il produira lors de l'exécution:
sont disposés à stocker ses éléments dans une liste intermédiaire:
peut créer deux générateurs identiques, parcourir le premier pour trouver la longueur totale, initialiser le tableau, puis parcourir à nouveau le générateur pour trouver chaque élément:
1 est probablement ce que vous recherchez. 2 est inefficace dans l'espace et 3 est inefficace dans le temps (vous devez passer deux fois par le générateur).
la source
array.array
est une liste contiguë non liée, et vous pouvez simplementarray.array('f', generator)
. Dire que c'est impossible est trompeur. C'est juste une allocation dynamique.Un google derrière ce résultat de stackoverflow, j'ai trouvé qu'il y avait un fichier
numpy.fromiter(data, dtype, count)
. La valeur par défautcount=-1
prend tous les éléments de l'itérable. Il faut que adtype
soit défini explicitement. Dans mon cas, cela a fonctionné:numpy.fromiter(something.generate(from_this_input), float)
la source
numpy.fromiter(gimme(), float, count=-1)
ne marche pas. Que signifiesomething
?numpy.fromiter(gimme(), float, count=-1)
fonctionne pour moi.fromiter
ne fonctionne que sur les tableaux 1D: mail.scipy.org/pipermail/numpy-discussion/2007-August/… .count=-1
n'a pas besoin d'être spécifié, car c'est la valeur par défaut.count
pour améliorer les performances. De cette façon, il alloue la mémoire avant de la remplir avec des valeurs plutôt que de le redimensionner à la demande (voir la documentation denumpy.fromiter
)Bien que vous puissiez créer un tableau 1D à partir d'un générateur avec
numpy.fromiter()
, vous pouvez créer un tableau ND à partir d'un générateur avecnumpy.stack
:Cela fonctionne également pour les baies 1D:
Notez que cela
numpy.stack
consomme en interne le générateur et crée une liste intermédiaire avecarrays = [asanyarray(arr) for arr in arrays]
. La mise en œuvre peut être trouvée ici .la source
np.array(tuple(mygen))
. Voici les résultats des tests:%timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loop
comparés à%timeit np.array(tuple(permutations(range(10), 7))) 1 loop, best of 3: 427 ms per loop
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.
Un peu tangentiel, mais si votre générateur est une compréhension de liste, vous pouvez l'utiliser
numpy.where
pour obtenir plus efficacement votre résultat (j'ai découvert cela dans mon propre code après avoir vu cet article)la source
Les fonctions vstack , hstack et dstack peuvent prendre comme générateurs d'entrée qui produisent des tableaux multidimensionnels.
la source