Вопрос

Я генерирую список одномерных массивов Numpy в цикле, а затем преобразую этот список в 2 -й массив Numpy. Я бы предположил 2 -й массив Numpy, если бы я знал количество предметов заранее, но я этого не делаю, поэтому я поместил все в список.

Макет внизу:

>>> list_of_arrays = map(lambda x: x*ones(2), range(5))
>>> list_of_arrays
[array([ 0.,  0.]), array([ 1.,  1.]), array([ 2.,  2.]), array([ 3.,  3.]), array([ 4.,  4.])]
>>> arr = array(list_of_arrays)
>>> arr
array([[ 0.,  0.],
       [ 1.,  1.],
       [ 2.,  2.],
       [ 3.,  3.],
       [ 4.,  4.]])

Мой вопрос - следующее:

Есть ли лучший способ (производительность), чтобы выполнить задачу сбора последовательных численных данных (в моем случае массивов Numpy), чем их в списке, а затем сделать из него Numpy.Array (я создаю новый OBJ и копирую данные)? Существует ли «расширяемая» структура данных матрицы в хорошо протестированном модуле?

Типичный размер моей 2D -матрицы будет между поплавками 100x10 и 5000x10

РЕДАКТИРОВАТЬ: В этом примере я использую карту, но в моем фактическом приложении у меня есть петля

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

Решение

Предположим, вы знаете, что окончательный массив arr никогда не будет больше 5000x10. Затем вы можете предварительно выделить массив максимального размера, заполнить его данными по мере прохождения петли, а затем использовать arr.resize Чтобы сократить его до обнаруженного размера после выхода из петли.

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

Также, arr.resize деонижает неиспользованную память, поэтому окончательный (хотя, возможно, не промежуточный) следов памяти меньше, чем используется python_lists_to_array.

Это показывает numpy_all_the_way быстрее:

% python -mtimeit -s"import test" "test.numpy_all_the_way(100)"
100 loops, best of 3: 1.78 msec per loop
% python -mtimeit -s"import test" "test.numpy_all_the_way(1000)"
100 loops, best of 3: 18.1 msec per loop
% python -mtimeit -s"import test" "test.numpy_all_the_way(5000)"
10 loops, best of 3: 90.4 msec per loop

% python -mtimeit -s"import test" "test.python_lists_to_array(100)"
1000 loops, best of 3: 1.97 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(1000)"
10 loops, best of 3: 20.3 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(5000)"
10 loops, best of 3: 101 msec per loop

Это показывает numpy_all_the_way использует меньше памяти:

% test.py
Initial memory usage: 19788
After python_lists_to_array: 20976
After numpy_all_the_way: 20348

test.py:

import numpy as np
import os


def memory_usage():
    pid = os.getpid()
    return next(line for line in open('/proc/%s/status' % pid).read().splitlines()
                if line.startswith('VmSize')).split()[-2]

N, M = 5000, 10


def python_lists_to_array(k):
    list_of_arrays = list(map(lambda x: x * np.ones(M), range(k)))
    arr = np.array(list_of_arrays)
    return arr


def numpy_all_the_way(k):
    arr = np.empty((N, M))
    for x in range(k):
        arr[x] = x * np.ones(M)
    arr.resize((k, M))
    return arr

if __name__ == '__main__':
    print('Initial memory usage: %s' % memory_usage())
    arr = python_lists_to_array(5000)
    print('After python_lists_to_array: %s' % memory_usage())
    arr = numpy_all_the_way(5000)
    print('After numpy_all_the_way: %s' % memory_usage())

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

Удобный способ, используя numpy.concatenate. Анкет Я полагаю, что это также быстрее, чем @unutbu's Ответ:

In [32]: import numpy as np 

In [33]: list_of_arrays = list(map(lambda x: x * np.ones(2), range(5)))

In [34]: list_of_arrays
Out[34]: 
[array([ 0.,  0.]),
 array([ 1.,  1.]),
 array([ 2.,  2.]),
 array([ 3.,  3.]),
 array([ 4.,  4.])]

In [37]: shape = list(list_of_arrays[0].shape)

In [38]: shape
Out[38]: [2]

In [39]: shape[:0] = [len(list_of_arrays)]

In [40]: shape
Out[40]: [5, 2]

In [41]: arr = np.concatenate(list_of_arrays).reshape(shape)

In [42]: arr
Out[42]: 
array([[ 0.,  0.],
       [ 1.,  1.],
       [ 2.,  2.],
       [ 3.,  3.],
       [ 4.,  4.]])

Даже проще, чем ответ @gill Bates, вот код одной строки:

np.stack(list_of_arrays, axis=0)

То, что вы делаете, является стандартным способом. Свойство Numpy Arrays заключается в том, что им нужна смежная память. Единственная возможность «отверстий», о которых я могу подумать, возможно с strides член PyArrayObject, но это не влияет на обсуждение здесь. Поскольку массивы Numpy имеют смежную память и являются «предварительными», добавление новой строки/столбца означает выделение новой памяти, копирование данных, а затем освобождение старой памяти. Если вы сделаете это много, это не очень эффективно.

Один случай, когда кто -то может не захотеть создавать список, а затем преобразовать его в массив Numpy, в конце концов, - это когда список содержит много чисел: массив Numpy чисел занимает гораздо меньше места, чем нативный список Python (поскольку Нативный список Python хранит объекты Python). Для ваших типичных размеров массива я не думаю, что это проблема.

Когда вы создаете свой последний массив из списка массивов, вы находятся Копирование всех данных в новое место для нового (2-D в вашем примере) массива. Это все еще намного эффективнее, чем намель next = numpy.vstack((next, new_row)) Каждый раз, когда вы получаете новые данные. vstack() скопирует все данные для каждой «строки».

Был Тема в списке рассылки Numpy-Discussion Некоторое время назад, в которой обсуждалась возможность добавления нового типа массива Numpy, который позволяет эффективно расширять/добавлять. Кажется, в то время был значительный интерес, хотя я не знаю, вышло ли что -то из этого. Вы можете посмотреть на эту ветку.

Я бы сказал, что то, что вы делаете, очень питоническое и эффективное, поэтому, если вам действительно не нужно что -то еще (может быть, больше пространственно -эффективности?), У вас все будет в порядке. Вот как я создаю свои массивы Numpy, когда вначале я не знаю количество элементов в массиве.

Я добавлю свою собственную версию ответа ~ Unutbu. Аналогично Numpy_all_ The Way, но вы динамически изменяете размер, если у вас есть ошибка индекса. Я думал, что это было бы немного быстрее для небольших наборов данных, но это немного медленнее - проверка границ слишком сильно замедляет ситуацию.

initial_guess = 1000

def my_numpy_all_the_way(k):
    arr=np.empty((initial_guess,M))
    for x,row in enumerate(make_test_data(k)):
        try:
            arr[x]=row
        except IndexError:
            arr.resize((arr.shape[0]*2, arr.shape[1]))
            arr[x]=row
    arr.resize((k,M))
    return arr

Даже проще @fnjn ответ

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