【问题标题】:Numpy append: Automatically cast an array of the wrong dimensionNumpy追加:自动转换错误维度的数组
【发布时间】:2026-01-26 17:35:01
【问题描述】:

有没有办法在没有 if 子句的情况下执行以下操作?

我正在使用 pupynere 读取一组 netcdf 文件,并希望使用 numpy append 构建一个数组。有时输入数据是多维的(参见下面的变量“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()),但他(和其他人)指出代码效率低下。在下面的答案中,我结合了他们的建议并替换了我的原始代码。现在应该更有效率了。谢谢。

【问题讨论】:

    标签: python list performance numpy append


    【解决方案1】:

    你可以使用numpy.atleast_2d():

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

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

    【讨论】:

    • 感谢您的快速答复。有趣的是,我不知道 atleast_2d() 方法,但它似乎有效。但是,我认为您的意思是 np.atleast_2d(i)?关于分配,我不知道最终的大小,我还能做些什么来减少低效吗?
    • 如果性能很重要,您可以先收集所有要加入 Python 列表的数组,计算数组的最终大小,然后分配和填充数组。 (如果这种方法有任何问题,请提出一个新问题。)
    • @Sven,回复:存储数组。在大多数情况下,这可能比我的建议更好(假设有足够的内存来存储所有数组两次)。
    • @Sven @Henry,非常感谢您的帮助和提示。如果遇到速度问题,我会尝试使用 python 列表方法。 ciao!
    • @Sebastian:再想一想——也许在创建上述列表后调用numpy.vstack() 是您的最佳猜测。
    【解决方案2】:

    正如所指出的,append 需要重新分配每个 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]
    

    【讨论】:

    • 不错的sn-p,谢谢。但是,这需要我打开每个文件(数百个)两次,对吗?首先,获取大小(循环 1),其次提取数据(循环 2)。将文件(指针?)存储在内存中也可能效率不高(但我无法判断)。你怎么看?
    • 我不熟悉有问题的确切库,所以如果答案不能完全转移,请原谅我。如果重点是您每次都在加载文件,您能否从文件元数据中获得total_size(当然,文件大小将设置数组大小的上限,假设它没有被压缩)?关于时间上的差异,只是测量它! (timeit 模块使它非常简单)。顺便说一句,解决多次加载文件问题的最简单方法是按照@Sven 在他的 cmets 中对他的帖子提出的建议,将所有数组加载到内存中。
    【解决方案3】:

    您可以将所有数组添加到一个列表中,然后在最后使用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]])
    

    【讨论】:

    • 是的,这似乎可行,@Sven 也建议这样做。我不应该在我的 for 循环中调用 result = np.vstack(result,i) 的原因是效率低下,对吧?
    【解决方案4】:

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

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

    假设iab 类型:

    > 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)
    

    再次感谢您的宝贵帮助。

    【讨论】:

    • 使用np.vstack()时不需要np.atleast_2d(),但危害不大。
    • @Robert 哦,你是对的。太好了,这使它更加紧凑。我会改变我的答案。
    最近更新 更多