Вопрос

Как я могу создать массив numpy из объекта generator?

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

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

Это было полезно?

Решение

Массивы 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 неэффективно по времени (вам придется дважды пройти через генератор).

Другие советы

Используя Google для получения этого результата stackoverflow, я обнаружил, что существует numpy.fromiter(data, dtype, count).Значение по умолчанию count=-1 принимает все элементы из iterable.Для этого требуется dtype должен быть задан явно.В моем случае это сработало:

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

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

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

Это также работает для одномерных массивов:

>>> 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].Реализацию можно найти здесь.

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

Тот Самый встэк, hstack ( хстак ), и дстак функции могут принимать в качестве входных данных генераторы, которые выдают многомерные массивы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top