python - tutorial - ¿Cómo construyo una matriz numpy de un generador?
numpy tutorial español pdf (4)
¿Cómo puedo construir una matriz numpy 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 la matriz no itera sobre el generador, simplemente almacena el generador. El comportamiento que deseo es el de 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 uso del espacio?
Algo tangencial, pero si su generador es una lista de comprensión, puede usar numpy.where
para obtener más efectivamente su resultado (descubrí esto en mi propio código después de ver esta publicación)
Las matrices de 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 las matrices numpy: esto combinado con la implementación del código nativo permite que las operaciones en ellas 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 arrojará cuando se ejecute:
my_array = numpy.empty(predict_length()) for i, el in enumerate(gimme()): my_array[i] = el
están dispuestos a almacenar sus elementos en una lista intermedia:
my_array = numpy.array(list(gimme()))
puede hacer dos generadores idénticos, ejecutar el primero para encontrar la longitud total, inicializar la matriz y luego volver a ejecutar el generador para encontrar cada elemento:
length = sum(1 for el in gimme()) my_array = numpy.empty(length) for i, el in enumerate(gimme()): my_array[i] = el
1 es probablemente lo que estás buscando. 2 es ineficiente en el espacio, y 3 es ineficiente en el tiempo (tienes que pasar por el generador dos veces).
Si bien puede crear una matriz 1D a partir de un generador con numpy.fromiter()
, puede crear una matriz ND a partir de un generador con numpy.stack
:
>>> mygen = (np.ones((5, 3)) for _ in range(10))
>>> x = numpy.stack(mygen)
>>> x.shape
(10, 5, 3)
También funciona para matrices 1D:
>>> numpy.stack(2*i for i in range(10))
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
Tenga en cuenta que numpy.stack
consume internamente el generador y crea una lista intermedia con arrays = [asanyarray(arr) for arr in arrays]
. La implementación se puede encontrar here .
Un google detrás de este resultado de , encontré que hay un numpy.fromiter(data, dtype, count)
. El count=-1
predeterminado count=-1
toma todos los elementos del iterable. Requiere un dtype
para establecerse explícitamente. En mi caso, esto funcionó:
numpy.fromiter(something.generate(from_this_input), float)