Numpy追加:自动投射错误维度的数组

时间:2011-04-20 11:59:32

标签: python list performance numpy append

有没有if子句可以执行以下操作的方法?

我正在阅读一组带有pupynere的netcdf文件,并希望构建一个带有numpy追加的数组。有时输入数据是多维的(见下面的变量“a”),有时是一维(“b”),但第一维中的元素数总是相同的(下例中的“9”)。

> import numpy as np
> a = np.arange(27).reshape(3,9)
> b = np.arange(9)
> a.shape
(3, 9)
> b.shape
(9,)

这可以按预期工作:

> np.append(a,a, axis=0)
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
   [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
   [18, 19, 20, 21, 22, 23, 24, 25, 26],
   [ 0,  1,  2,  3,  4,  5,  6,  7,  8],
   [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
   [18, 19, 20, 21, 22, 23, 24, 25, 26]])

但是,附加b不能如此优雅地工作:

> np.append(a,b, axis=0)
ValueError: arrays must have same number of dimensions

追加的问题是(来自numpy手册)

  

"When axis is specified, values must have the correct shape."

为了得到正确的结果,我必须先施展。

> np.append(a,b.reshape(1,9), axis=0)
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
   [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
   [18, 19, 20, 21, 22, 23, 24, 25, 26],
   [ 0,  1,  2,  3,  4,  5,  6,  7,  8]])

所以,在我的文件读取循环中,我正在使用这样的if子句:

for i in [a, b]:
    if np.size(i.shape) == 2:
        result = np.append(result, i, axis=0)
    else:
        result = np.append(result, i.reshape(1,9), axis=0)

有没有办法在没有if语句的情况下附加“a”和“b”?

编辑:虽然@Sven完美地回答了原始问题(使用np.atleast_2d()),但他(和其他人)指出代码效率低下。在下面的答案中,我结合了他们的建议并替换了我的原始代码。它现在应该更有效率。感谢。

4 个答案:

答案 0 :(得分:3)

您可以使用numpy.atleast_2d()

result = np.append(result, np.atleast_2d(i), axis=0)

那就是说,注意重复使用numpy.append()是构建NumPy数组的一种非常低效的方法 - 它必须在每一步中重新分配。如果可能的话,使用所需的最终大小预先分配数组,然后使用切片填充它。

答案 1 :(得分:2)

您可以将所有数组添加到列表中,然后使用np.vstack()在最后将它们连接在一起。这避免了每次追加时不断重新分配不断增长的数组。

|1> a = np.arange(27).reshape(3,9)

|2> b = np.arange(9)

|3> np.vstack([a,b])
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
       [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23, 24, 25, 26],
       [ 0,  1,  2,  3,  4,  5,  6,  7,  8]])

答案 2 :(得分:1)

我将在@Sven,@ Henry和@Robert的帮助下改进我的代码。 @Sven回答了这个问题,所以他赢得了这个问题的声誉,但是 - 正如他和其他人所强调的那样 - 这是一种更有效的方式来做我想做的事。

这涉及使用python列表,允许附加performance penalty of O(1) whereas numpy.append() has a performance penalty of O(N**2)。之后,列表将转换为numpy数组:

假设i属于ab类型:

> a = np.arange(27).reshape(3,9)
> b = np.arange(9)
> a.shape
(3, 9)
> b.shape
(9,)

初始化列表并附加所有读取数据,例如如果数据按'aaba'的顺序出现。

> mList = []
> for i in [a,a,b,a]:
     mList.append(i)

您的mList将如下所示:

> mList
[array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
   [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
   [18, 19, 20, 21, 22, 23, 24, 25, 26]]),
 array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
   [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
   [18, 19, 20, 21, 22, 23, 24, 25, 26]]),
 array([0, 1, 2, 3, 4, 5, 6, 7, 8]),
 array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
   [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
   [18, 19, 20, 21, 22, 23, 24, 25, 26]])]

最后,vstack列表形成一个numpy数组:

> result = np.vstack(mList[:])
> result.shape
(10, 9)

再次感谢您提供宝贵的帮助。

答案 3 :(得分:0)

正如所指出的,追加需要重新分配每个numpy数组。分配一次的替代解决方案是这样的:

total_size = 0
for i in [a,b]:
    total_size += i.size

result = numpy.empty(total_size, dtype=a.dtype)
offset = 0
for i in [a,b]:
    # copy in the array
    result[offset:offset+i.size] = i.ravel()
    offset += i.size

# if you know its always divisible by 9:
result = result.reshape(result.size//9, 9)

如果你不能预先计算数组大小,那么也许你可以在大小上加上一个上限,然后只预先分配一个总是足够大的块。然后,您可以将结果视为该块的视图:

result = result[0:known_final_size]