Вопрос:

Pythonic способ создания массива numpy из списка массивов numpy

python performance arrays numpy scipy

37240 просмотра

6 ответа

2380 Репутация автора

Я генерирую список одномерных массивов numpy в цикле и позже конвертирую этот список в массив 2d numpy. Я бы предварительно выделил массив 2d 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 поплавков

EDIT: В этом примере я использую карту, но в моем фактическом приложении у меня есть цикл for

Автор: Dragan Chupacabric Источник Размещён: 21.01.2010 01:21

Ответы (6)


2 плюса

65020 Репутация автора

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

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

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

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

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

Автор: Alok Singhal Размещён: 21.01.2010 01:38

16 плюса

530703 Репутация автора

Решение

Предположим, вы знаете, что последний массив 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())
Автор: unutbu Размещён: 21.01.2010 03:09

2 плюса

5636 Репутация автора

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

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
Автор: wisty Размещён: 21.01.2010 06:58

18 плюса

1566 Репутация автора

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

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 Размещён: 16.01.2016 06:49

7 плюса

91 Репутация автора

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

np.stack(list_of_arrays, axis=0)
Автор: fnjn Размещён: 06.03.2018 01:34

0 плюса

356 Репутация автора

Еще проще @fnjn ответ

np.vstack(list_of_arrays)
Автор: Анатолий Панин Размещён: 19.10.2018 12:56
Вопросы из категории :
32x32