numpy如何为嵌套数组分配内存?

时间:2017-11-28 15:39:08

标签: python numpy

以下程序从嵌套的数组列表中创建一个大型数组:

import numpy as np
a = np.arange(6).reshape(2, 3)
nested_list = [[a, a + 1], [a + 2, a + 3]]
b = np.array(nested_list)

在这种情况下,在将数据复制到内存之前,np.array是否只为结果预分配一次内存?

或者,这类似于:

c = np.vstack([np.hstack([a, a + 1]), np.hstack([a + 2, a + 3])])

哪会预先分配内存3次?

>>> b
array([[[[0, 1, 2],
         [3, 4, 5]],

        [[1, 2, 3],
         [4, 5, 6]]],


       [[[2, 3, 4],
         [5, 6, 7]],

        [[3, 4, 5],
         [6, 7, 8]]]])
>>> c
array([[0, 1, 2, 1, 2, 3],
       [3, 4, 5, 4, 5, 6],
       [2, 3, 4, 3, 4, 5],
       [5, 6, 7, 6, 7, 8]])
>>> b.shape
(2, 2, 2, 3)
>>> b.reshape(2*2, 2*3)
array([[0, 1, 2, 3, 4, 5],
       [1, 2, 3, 4, 5, 6],
       [2, 3, 4, 5, 6, 7],
       [3, 4, 5, 6, 7, 8]])

1 个答案:

答案 0 :(得分:1)

nested_list = [[a, a + 1], [a + 2, a + 3]]生成3个新数组(总和)以及指向这些数组的指针列表。这只是基本的Python解释器操作。

b = np.array(nested_list)np.array是一个复杂的编译函数,所以如果不进行一些严肃的挖掘,很难确切地知道它的作用。我之前使用过的印象,尤其是组件在大小上完全匹配时的错误,是它扫描输入以确定它可以创建的最高维数组,然后插入片段,如果类型转换,需要的。

比较容易进行时间比较;更难跟踪内存使用情况。但假设数据复制是最大的时间消费者,时间测试可能是内存使用的良好代理。除非我们遇到内存错误,否则我们通常更关心时间而不是内存使用。

In [565]: alist = [[a,a+1],[a+2,a+3]]
In [566]: allist = [[a.tolist(), (a+1).tolist()],[(a+2).tolist(), (a+3).tolist()]]

In [567]: timeit np.array(alist)
6.74 µs ± 63.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [568]: timeit np.array(allist)
9.92 µs ± 286 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

使用嵌套的数组列表比使用纯列表等效工作要快一些。它可能是将这些数组作为块复制到目标。

单个堆栈明显较慢,但它也会创建a+n数组:

In [569]: timeit c = np.vstack([np.hstack([a, a + 1]), np.hstack([a + 2, a + 3])])
37.8 µs ± 39 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

np.stacknp.array的行为相同(使用默认轴)。它也使用concatenate

In [570]: timeit np.stack(alist)
28.7 µs ± 262 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

a+n计算纳入时间可能更公平:

In [571]: %%timeit
     ...: alist = [[a,a+1],[a+2,a+3]]
     ...: np.stack(alist)
     ...: 
38.6 µs ± 509 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [572]: %%timeit
     ...: alist = [[a,a+1],[a+2,a+3]]
     ...: np.array(alist)
     ...: 
15.7 µs ± 177 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

提到了新的np.block - 它会产生一些不同的东西并且速度相当慢

In [573]: np.block(alist)
Out[573]: 
array([[0, 1, 2, 1, 2, 3],
       [3, 4, 5, 4, 5, 6],
       [2, 3, 4, 3, 4, 5],
       [5, 6, 7, 6, 7, 8]])
In [574]: timeit np.block(alist)
126 µs ± 2.39 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

block生成与嵌套堆栈相同的2d数组:

np.vstack([np.hstack([a, a + 1]), np.hstack([a + 2, a + 3])])

np.arraynp.stack生成一个4d数组。它可以重新变形为2d,但元素的顺序是不同的。为了匹配我们需要在重塑之前进行一些转置。 e.g。

In [590]: np.array(alist).transpose(0,2,1,3).reshape(4,6)
Out[590]: 
array([[0, 1, 2, 1, 2, 3],
       [3, 4, 5, 4, 5, 6],
       [2, 3, 4, 3, 4, 5],
       [5, 6, 7, 6, 7, 8]])