Como criar uma matriz numpy a partir de um objeto gerador?
Deixe-me ilustrar o 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])
Neste exemplo, gimme()
é o gerador cuja saída eu gostaria de transformar em uma matriz. No entanto, o construtor do array não interage com o gerador, simplesmente armazena o próprio gerador. O comportamento que desejo é o denumpy.array(list(gimme()))
, mas não quero gastar a memória de ter a lista intermediária e a matriz final na memória ao mesmo tempo. Existe uma maneira mais eficiente em termos de espaço?
from numpy import *; print any(False for i in range(1))
- o que sombreia o built-inany()
e produz o resultado oposto (como eu sei agora).numpy
não puder (ou não quiser) tratar os geradores como o Python, pelo menos deve gerar uma exceção quando receber um gerador como argumento.Respostas:
Arrays numpy exigem que seu comprimento seja definido explicitamente no momento da criação, ao contrário das listas python. Isso é necessário para que o espaço para cada item possa ser alocado consecutivamente na memória. A alocação consecutiva é o principal recurso de matrizes numpy: isso combinado à implementação de código nativo permite que as operações nelas executem muito mais rapidamente do que as listas regulares.
Tendo isso em mente, é tecnicamente impossível pegar um objeto gerador e transformá-lo em uma matriz, a menos que você:
pode prever quantos elementos ele produzirá quando executado:
estão dispostos a armazenar seus elementos em uma lista intermediária:
pode criar dois geradores idênticos, percorrer o primeiro para encontrar o comprimento total, inicializar a matriz e, em seguida, percorrer o gerador novamente para encontrar cada elemento:
1 é provavelmente o que você está procurando. 2 é ineficiente em espaço e 3 é ineficiente em tempo (você precisa passar pelo gerador duas vezes).
fonte
array.array
é uma lista não vinculada contínua e você pode simplesmentearray.array('f', generator)
. Dizer que é impossível é enganoso. É apenas alocação dinâmica.Um google por trás desse resultado de stackoverflow, descobri que existe um
numpy.fromiter(data, dtype, count)
. O padrãocount=-1
pega todos os elementos do iterável. Requer que umdtype
seja definido explicitamente. No meu caso, isso funcionou:numpy.fromiter(something.generate(from_this_input), float)
fonte
numpy.fromiter(gimme(), float, count=-1)
não funciona. O quesomething
significa?numpy.fromiter(gimme(), float, count=-1)
funciona para mim.fromiter
funciona apenas em matrizes 1D: mail.scipy.org/pipermail/numpy-discussion/2007-August/… .count=-1
não precisa ser especificado, pois é o padrão.count
para melhorar o desempenho. Desta forma, ele aloca a memória antes de o encher com valores em vez de redimensionamento on demand (consulte a documentação denumpy.fromiter
)Enquanto você pode criar uma matriz 1D a partir de um gerador com
numpy.fromiter()
, você pode criar uma matriz ND a partir de um gerador comnumpy.stack
:Também funciona para matrizes 1D:
Observe que
numpy.stack
está consumindo internamente o gerador e criando uma lista intermediária comarrays = [asanyarray(arr) for arr in arrays]
. A implementação pode ser encontrada aqui .fonte
np.array(tuple(mygen))
. Aqui estão os resultados dos testes: em%timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loop
comparação com%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.
Um pouco tangencial, mas se o seu gerador é uma lista de compreensão, você pode usar
numpy.where
para obter o resultado com mais eficiência (eu descobri isso no meu próprio código depois de ver este post)fonte
As funções vstack , hstack e dstack podem assumir como geradores de entrada que produzem matrizes multidimensionais.
fonte