Как я могу построить массив из объекта генератора?
Позвольте мне проиллюстрировать проблему:
>>> 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])
В данном случае gimme()
это генератор, вывод которого я бы хотел превратить в массив. Однако конструктор массива не выполняет итерацию по генератору, он просто хранит сам генератор. Я хочу получить такое поведение numpy.array(list(gimme()))
, но я не хочу платить за использование промежуточного списка и окончательного массива в памяти одновременно. Есть ли более экономичный способ?
from numpy import *; print any(False for i in range(1))
- который затеняет встроенноеany()
и дает противоположный результат (как я знаю сейчас).numpy
не может (или не хочет) обращаться с генераторами, как это делает Python, по крайней мере, он должен вызвать исключение, когда получает генератор в качестве аргумента.Ответы:
Для массивов Numpy их длина должна быть задана явно во время создания, в отличие от списков Python. Это необходимо для того, чтобы пространство для каждого элемента могло быть последовательно выделено в памяти. Последовательное распределение является ключевой особенностью массивов numpy: это в сочетании с реализацией собственного кода позволяет выполнять над ними операции намного быстрее, чем обычные списки.
Помня об этом, технически невозможно взять объект-генератор и превратить его в массив, если только вы не:
может предсказать, сколько элементов он выдаст при запуске:
готовы хранить его элементы в промежуточном списке:
Можно создать два идентичных генератора, выполнить первый, чтобы найти общую длину, инициализировать массив, а затем снова запустить генератор, чтобы найти каждый элемент:
1 , вероятно, то, что вы ищете. 2 - неэффективное пространство, а 3 - неэффективное время (вы должны пройти через генератор дважды).
источник
array.array
список представляет собой непрерывный несвязанный список, и вы можете простоarray.array('f', generator)
. Сказать, что это невозможно, вводит в заблуждение. Это просто динамическое распределение.Один гугл за этим результатом переполнения стека, я обнаружил, что есть
numpy.fromiter(data, dtype, count)
. По умолчаниюcount=-1
берут все элементы из итерируемого. Требуетсяdtype
установить явно. В моем случае это сработало:numpy.fromiter(something.generate(from_this_input), float)
источник
numpy.fromiter(gimme(), float, count=-1)
не работает. Что означаетsomething
?numpy.fromiter(gimme(), float, count=-1)
работает для меня.fromiter
работает только на одномерных массивах: mail.scipy.org/pipermail/numpy-discussion/2007-August/… .count=-1
не нужно, так как это значение по умолчанию.count
для повышения производительности. Таким образом, он выделяет память перед заполнением ее значениями, а не изменяет размеры по требованию (см. Документациюnumpy.fromiter
)Хотя вы можете создать массив 1D из генератора с помощью
numpy.fromiter()
, вы можете создать массив ND из генератора с помощьюnumpy.stack
:Это также работает для 1D массивов:
Обратите внимание, что он
numpy.stack
использует внутренний генератор и создает промежуточный список с помощьюarrays = [asanyarray(arr) for arr in arrays]
. Реализация может быть найдена здесь .источник
np.array(tuple(mygen))
. Вот результаты теста:%timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loop
по сравнению с%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.
В некоторой степени тангенциально, но если ваш генератор предназначен для понимания списков, вы можете использовать его
numpy.where
для более эффективного получения результата (я обнаружил это в своем собственном коде после просмотра этого поста)источник
Функции vstack , hstack и dstack могут принимать в качестве входных генераторов, которые выдают многомерные массивы.
источник