从numpy数组列表创建numpy数组的Pythonic方法

从numpy数组列表创建numpy数组的Pythonic方法,python,performance,arrays,numpy,scipy,Python,Performance,Arrays,Numpy,Scipy,我在循环中生成一个一维numpy数组列表,然后将该列表转换为二维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.])

我在循环中生成一个一维numpy数组列表,然后将该列表转换为二维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个浮点数


编辑:在本例中,我使用的是map,但在我的实际应用程序中,我有一个for循环

您所做的是标准方式。numpy数组的一个特性是它们需要连续内存。我能想到的唯一可能的“漏洞”是
PyArrayObject
strips
成员,但这并不影响这里的讨论。由于numpy数组具有连续内存并且是“预分配的”,因此添加新行/列意味着分配新内存、复制数据,然后释放旧内存。如果你经常这样做,效率就不高

有一种情况是,可能有人不希望创建一个列表,然后最终将其转换为numpy数组,即当列表包含大量数字时:numpy数字数组比本机Python数字列表占用的空间小得多(因为本机Python列表存储Python对象)。对于典型的阵列大小,我认为这不是问题

从阵列列表创建最终阵列时,您正在将所有数据复制到新阵列(在示例中为二维)的新位置。这仍然比使用numpy数组并在每次获得新数据时执行
next=numpy.vstack((next,new_row))
更有效
vstack()
将复制每个“行”的所有数据

不久前有一次讨论了添加一种新的numpy数组类型的可能性,该类型允许有效的扩展/追加。当时人们似乎对这件事很感兴趣,尽管我不知道是否有什么结果。你可能想看看那条线


我想说的是,你所做的是非常有益的,高效的,所以除非你真的需要其他东西(可能更节省空间?),否则你应该没事。这就是我在开始时不知道数组中的元素数时创建numpy数组的方式。

假设您知道最终数组
arr
永远不会大于5000x10。 然后,您可以预先分配一个最大大小的数组,并根据需要使用数据填充它 您先遍历循环,然后使用
arr.resize
将其缩减到最小值 退出循环后发现的大小

下面的测试表明,这样做会比构建中间层稍微快一些 无论数组的最终大小如何,python都会列出

另外,
arr.resize
de分配未使用的内存,因此最终(尽管可能不是中间)内存占用比
python\u list\u to\u array使用的内存占用要小

这显示了
numpy\u所有方式
都更快:

% 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\u所有方式
使用的内存更少:

% 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的答案。与numpy_类似,但如果出现索引错误,可以动态调整大小。我原以为对于小数据集来说速度会快一点,但是速度会慢一点——边界检查会让速度慢很多

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
方便的方式,使用。我相信这也比@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的答案更简单,这里有一行代码:

np.stack(list_of_arrays, axis=0)

更简单的@fnjn答案

np.vstack(list_of_arrays)

@谢谢你周到的回答。~unubuntu的答案中的计时显示出对5%效率的担忧。这几乎肯定是一个错误,直到你达到一定的程度,你绝对必须有这5%。好吧,因为@fnjn的答案中的
axis=0
是默认参数,它可以省略,他的一个不太明确的版本,
np.stack(数组列表)
,更简单。但我想这不是代码高尔夫;)需要注意的是,
python\u lists\u to\u array
假设使用python 2,其中
map
生成一个
列表
@hpaulj:感谢您的更正。Python3的代码现在已经更新。您认为有没有办法跳过
列表
部分?我问了一个这样的问题,