¿Cómo puedo construir una matriz numpy a partir de un objeto generador?
Déjame ilustrar el 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])
En este caso, gimme()
es el generador cuya salida me gustaría convertir en una matriz. Sin embargo, el constructor de matriz no itera sobre el generador, simplemente almacena el generador en sí. El comportamiento que deseo es ese numpy.array(list(gimme()))
, pero no quiero pagar la sobrecarga de memoria de tener la lista intermedia y la matriz final en la memoria al mismo tiempo. ¿Hay una manera más eficiente en el espacio?
from numpy import *; print any(False for i in range(1))
, que sombrea el incorporadoany()
y produce el resultado opuesto (como lo sé ahora).numpy
no puede (o no quiere) tratar a los generadores como lo hace Python, al menos debería generar una excepción cuando recibe un generador como argumento.Respuestas:
Las matrices Numpy requieren que su longitud se establezca explícitamente en el momento de la creación, a diferencia de las listas de Python. Esto es necesario para que el espacio para cada elemento se pueda asignar consecutivamente en la memoria. La asignación consecutiva es la característica clave de los arreglos numpy: esto combinado con la implementación de código nativo permite que las operaciones en ellos se ejecuten mucho más rápido que las listas regulares.
Teniendo esto en cuenta, es técnicamente imposible tomar un objeto generador y convertirlo en una matriz a menos que:
puede predecir cuántos elementos producirá cuando se ejecute:
están dispuestos a almacenar sus elementos en una lista intermedia:
puede hacer dos generadores idénticos, ejecutar el primero para encontrar la longitud total, inicializar la matriz y luego ejecutar el generador nuevamente para encontrar cada elemento:
1 es probablemente lo que estás buscando. 2 es ineficiente en espacio y 3 es ineficiente en tiempo (debe pasar por el generador dos veces).
fuente
array.array
es una lista contigua no vinculada, y puede simplementearray.array('f', generator)
. Decir decir que es imposible es engañoso. Es solo una asignación dinámica.Un google detrás de este resultado stackoverflow, encontré que hay un
numpy.fromiter(data, dtype, count)
. El valor predeterminadocount=-1
toma todos los elementos del iterable. Requieredtype
que se establezca explícitamente. En mi caso, esto funcionó:numpy.fromiter(something.generate(from_this_input), float)
fuente
numpy.fromiter(gimme(), float, count=-1)
No funciona. ¿Qué significasomething
?numpy.fromiter(gimme(), float, count=-1)
funciona para mí.fromiter
solo funciona en matrices 1D: mail.scipy.org/pipermail/numpy-discussion/2007-August/… .count=-1
no necesita ser especificado, ya que es el predeterminado.count
para mejorar el rendimiento. De esta manera, asigna la memoria antes de llenarla con valores en lugar de cambiar el tamaño a pedido (consulte la documentación denumpy.fromiter
)Si bien puede crear una matriz 1D desde un generador con
numpy.fromiter()
, puede crear una matriz ND desde un generador connumpy.stack
:También funciona para matrices 1D:
Tenga en cuenta que
numpy.stack
está consumiendo internamente el generador y creando una lista intermedia conarrays = [asanyarray(arr) for arr in arrays]
. La implementación se puede encontrar aquí .fuente
np.array(tuple(mygen))
. Aquí están los resultados de la prueba: en%timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loop
comparación con%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.
Algo tangencial, pero si su generador es una comprensión de la lista, puede usarlo
numpy.where
para obtener su resultado de manera más efectiva (descubrí esto en mi propio código después de ver esta publicación)fuente
Las funciones vstack , hstack y dstack pueden tomarse como generadores de entrada que producen matrices multidimensionales.
fuente