Как мне построить массив из генератора? - PullRequest
135 голосов
/ 15 декабря 2008

Как я могу построить массив из объекта генератора?

Позвольте мне проиллюстрировать проблему:

>>> 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 ())), но я не хочу платить за использование промежуточного списка и окончательного массива в памяти одновременно. Есть ли более экономичный способ?

Ответы [ 5 ]

181 голосов
/ 24 февраля 2009

Один гугл за этим результатом переполнения стека, я обнаружил, что есть numpy.fromiter(data, dtype, count). По умолчанию count=-1 принимает все элементы из итерируемого. Требуется dtype, чтобы быть установленным явно. В моем случае это сработало:

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

114 голосов
/ 15 декабря 2008

Для массивов Numpy требуется, чтобы их длина была задана явно во время создания, в отличие от списков Python. Это необходимо для того, чтобы место для каждого элемента могло быть последовательно выделено в памяти. Последовательное распределение является ключевой особенностью массивов numpy: это в сочетании с реализацией собственного кода позволяет выполнять над ними операции намного быстрее, чем обычные списки.

Имея это в виду, технически невозможно взять объект-генератор и превратить его в массив, если вы не либо:

  1. может предсказать, сколько элементов он выдаст при запуске:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
    
  2. готовы хранить его элементы в промежуточном списке:

    my_array = numpy.array(list(gimme()))
    
  3. может создать два одинаковых генератора, выполнить первый, чтобы найти общую длину, инициализировать массив, а затем снова запустить генератор, чтобы найти каждый элемент:

    length = sum(1 for el in gimme())
    my_array = numpy.empty(length)
    for i, el in enumerate(gimme()): my_array[i] = el
    

1 - это, вероятно, то, что вы ищете. 2 - неэффективное пространство, а 3 - неэффективное время (вы должны пройти через генератор дважды).

10 голосов
/ 31 августа 2017

Хотя вы можете создать массив 1D из генератора с numpy.fromiter(), вы можете создать массив N-D из генератора с numpy.stack:

>>> mygen = (np.ones((5, 3)) for _ in range(10))
>>> x = numpy.stack(mygen)
>>> x.shape
(10, 5, 3)

Это также работает для 1D массивов:

>>> numpy.stack(2*i for i in range(10))
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

Обратите внимание, что numpy.stack использует генератор изнутри и создает промежуточный список с arrays = [asanyarray(arr) for arr in arrays]. Реализацию можно найти здесь .

5 голосов
/ 13 мая 2009

В некоторой степени тангенциально, но если ваш генератор предназначен для понимания списка, вы можете использовать numpy.where для более эффективного получения вашего результата (я обнаружил это в своем собственном коде после просмотра этого поста)

0 голосов
/ 14 ноября 2018

Функции vstack , hstack и dstack могут принимать в качестве входных генераторов, которые дают многомерные массивы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...