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

时间:2010-01-21 01:21:06

标签: python performance arrays numpy scipy

我在循环中生成一维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浮点之间

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

6 个答案:

答案 0 :(得分:19)

方便,使用numpy.concatenate。我相信它也比@ unutbu的答案更快:

on idle
    set msgboxPath to "/Applications/VideoHost.app/Contents/Resources/MsgBox.nib"

    tell application "System Events"
        if exists file msgboxPath then set name of file msgboxPath to "No_MsgBox.nib"
    end tell

    return 60
end idle

答案 1 :(得分:18)

假设您知道最终数组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())

答案 2 :(得分:12)

比@Gill Bates的答案更简单,这里是一行代码:

np.stack(list_of_arrays, axis=0)

答案 3 :(得分:2)

您正在做的是标准方式。 numpy数组的一个属性是它们需要连续的内存。 strides PyArrayObject next = numpy.vstack((next, new_row))成员可能会想到“漏洞”的唯一可能性,但这不会影响此处的讨论。由于numpy数组具有连续的内存并且是“预分配的”,因此添加新的行/列意味着分配新内存,复制数据,然后释放旧内存。如果你做了很多,那就不是很有效了。

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

从阵列列表创建最终阵列时, 将所有数据复制到新位置(示例中为2-d)的新位置。这比使用numpy数组和每次获取新数据时vstack()更有效。 {{1}}将复制每个“行”的所有数据。

前段时间有一个thread on numpy-discussion mailing list讨论了添加一个允许有效扩展/追加的新numpy数组类型的可能性。当时似乎对此有很大的兴趣,虽然我不知道是否有什么事情发生了。你可能想看看那个帖子。

我会说你正在做的是非常Pythonic,而且效率很高,所以除非你真的需要别的东西(更多的空间效率,也许吧?),你应该没问题。这就是我在开始时不知道数组中元素数量时创建numpy数组的方法。

答案 4 :(得分:2)

我将添加我自己的~unutubu的答案版本。与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

答案 5 :(得分:2)

更简单的@fnjn答案

np.vstack(list_of_arrays)