Pregunta

¿Cómo puedo construir una matriz numpy de un objeto generador?

Permítanme 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, dame () es el generador cuya salida me gustaría convertir en una matriz. Sin embargo, el constructor matriz no iterar sobre el generador, simplemente almacena el propio generador. El comportamiento que deseo es que a partir de numpy.array (lista (Dame ())), pero no quiero pagar la sobrecarga de la memoria de tener la lista intermedia y la matriz final en la memoria al mismo tiempo. ¿Hay una manera más eficiente con el espacio?

¿Fue útil?

Solución

matrices numpy requieren su longitud para ajustarse de forma explícita 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 puede asignar de forma consecutiva en la memoria. asignación consecutiva es la característica clave de matrices numpy:. esto combinado con la implementación del código nativo dejó que las operaciones en los ejecutan 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 sea:

  1. puede predecir cuántos elementos se producirá cuando se ejecuta:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
    
  2. están dispuestos para almacenar sus elementos en una lista intermedio:

    my_array = numpy.array(list(gimme()))
    
  3. puede hacer dos generadores idénticos, ejecute a través de la primera uno para encontrar la longitud total, inicializar la matriz, y luego ejecutar a través del generador de nuevo 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 del espacio, y 3 es ineficaz del tiempo (hay que pasar por el generador de dos veces).

Otros consejos

Una Google detrás de este resultado stackoverflow, he encontrado que hay un numpy.fromiter(data, dtype, count) . El valor por defecto count=-1 toma todos los elementos de la iterable. Se requiere una dtype que se establezca explícitamente. En mi caso, esto funcionó:

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

Si bien se puede crear una matriz 1D de un generador con numpy.fromiter(), se puede crear una matriz N-D 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 arrays = [asanyarray(arr) for arr in arrays] está consumiendo internamente el generador y la creación de una lista intermedia con <=>. La aplicación se puede encontrar aquí .

Un poco tangencial, pero si el generador es una lista por comprensión, puede utilizar numpy.where para conseguir más eficazmente su resultado (descubrí esto en mi propio código después de ver este post)

El vstack , hstack , y dstack funciones pueden tomar como generadores de entrada que el rendimiento matrices multidimensionales.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top