Numpy 错误:形状不匹配

4 投票
4 回答
38552 浏览
提问于 2025-04-18 06:44

我在用Python(Numpy)解决一个科学问题时,遇到了一个“形状不匹配”的错误,提示是:“形状不匹配:对象无法广播到单一形状”。我把这个错误简化了一下,下面是我的代码:

import numpy as np
nx = 3; ny = 5
ff = np.ones([nx,ny,7])
def test(x, y):
    z = 0.0
    for i in range(7):
        z = z + ff[x,y,i]
    return z

print test(np.arange(nx),np.arange(ny))

当我用 test(x,y) 调用时,设置 x=1,y=np.arange(ny),一切都正常。那么这是怎么回事呢?为什么这两个参数不能都是numpy数组呢?

更新

在@Saullo Castro 的一些提示下,我找到了问题所在。对于那些想帮忙但不太明白我意图的人,这里有一些更新的信息:

基本上,我创建了一个大小为 nx*ny 的网格,并且还有一个数组 ff,它为每个节点存储了一些值。在上面的代码中,ff 对于每个节点有7个值,我想把这7个值加起来,得到一个新的 nx*ny 数组。

不过,“形状不匹配”的错误并不是因为求和过程,正如你们现在可能猜到的那样。其实我误解了函数接受 ndarray 对象作为输入参数的规则。我尝试把 np.arange(nx), np.arange(ny) 传给 test(),即使 nx==ny,也不会得到我想要的结果。

回到我最初的想法,我通过创建另一个函数,并使用 np.fromfunction 来创建数组,解决了这个问题:

def tt(x, y):  
    return np.fromfunction(lambda a,b: test(a,b), (x, y))

虽然这不是完美的解决方案,但它有效。(在这个例子中似乎没有必要创建一个新函数,但在我的实际代码中,我稍微修改了一下,这样它可以用于网格的切片)

总之,我相信还有比我这种“脏”解决方案更好的方法。如果你有任何想法,请和我们分享 :)。

4 个回答

0

在你的问题中,不太清楚你想要做什么或者你期待的结果是什么。不过,看起来你是想用你的变量 z 来计算一个总和。

检查一下 sum 方法是否能给你想要的结果:

import numpy as np
nx = 3; ny = 5

ff = ff = np.array(np.arange(nx*ny*7)).reshape(nx,ny,7)

print ff.sum()          # 5460

print ff.sum(axis=0)    # array([[105, 108, 111, 114, 117, 120, 123],
                        #        [126, 129, 132, 135, 138, 141, 144],
                        #        [147, 150, 153, 156, 159, 162, 165],
                        #        [168, 171, 174, 177, 180, 183, 186],
                        #        [189, 192, 195, 198, 201, 204, 207]]) shape(5,7)

print ff.sum(axis=1)    # array([[ 70,  75,  80,  85,  90,  95, 100],
                        #        [245, 250, 255, 260, 265, 270, 275],
                        #        [420, 425, 430, 435, 440, 445, 450]]) shape (3,7)

print ff.sum(axis=2)    # array([[ 21,  70, 119, 168, 217],
                        #        [266, 315, 364, 413, 462],
                        #        [511, 560, 609, 658, 707]]) shape (3,5)
0

我们来把你的问题简化成二维的情况,这样更容易理解:

import numpy as np

a = np.arange(15).reshape(3,5)

x = np.arange(3)
y = np.arange(5)

演示:

>>> a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>> a[x, y] # <- This is the error that you are getting
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: shape mismatch: objects cannot be broadcast to a single shape

# You are getting the error because x and y are different lengths,
# If x and y were the same lengths, the code would work: 

>>> a[x, x]
array([ 0,  6, 12])

# mixing arrays and scalars is not a problem

>>> a[x, 2]
array([ 2,  7, 12])
1

看起来你想对最后一个维度的 ff 进行求和,同时前两个维度要覆盖它们的整个范围。这里的 : 是用来表示某个维度的整个范围的。

def test():
    z = 0.0
    for i in range(7):
        z = z + ff[:,:,i]
    return z
print test()

不过,你可以通过使用 sum 方法来实现同样的结果,而不需要使用循环。

print ff.sum(axis=-1)

:0:n 的简写。

ff[0:nx, 0:ny, 0]==ff[:,:,0]

你可以用范围来索引一个 ff 的块,但在处理索引数组的形状时需要更加小心。对于初学者来说,最好先专注于正确使用 切片广播


编辑 -

你可以用 meshgrid 生成的数组来索引像 ff 这样的数组:

I,J = meshgrid(np.arange(nx),np.arange(ny),indexing='ij',sparse=False)
I.shape # (nx,ny)
ff[I,J,:]

也可以与

I,J = meshgrid(np.arange(nx),np.arange(ny),indexing='ij',sparse=True)
I.shape # (nx,1)
J.shape # (1, ny)

ogridmgrid 作为 meshgrid 的替代品。

3

我们来看一个和你的 ff 数组类似的数组:

nx = 3; ny = 4
ff = np.arange(nx*ny*5).reshape(nx,ny,5)
#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, 27, 28, 29],
#        [30, 31, 32, 33, 34],
#        [35, 36, 37, 38, 39]],
#
#       [[40, 41, 42, 43, 44],
#        [45, 46, 47, 48, 49],
#        [50, 51, 52, 53, 54],
#        [55, 56, 57, 58, 59]]])

当你用索引数组 a, b, c 来索引像 ff[a, b, c] 这样的数组时,a, b, c 必须具有相同的形状。然后,numpy 会根据这些索引构建一个新的数组。例如:

ff[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 2, 3], [0, 0, 0, 1, 1, 1]]
#array([ 0,  5, 20, 26, 51, 56])

这被称为“花式索引”,就像用以下方式构建一个数组:

np.array([ff[0, 0, 0], ff[0, 1, 0], ff[1, 0, 0], ..., ff[2, 3, 1]])

在你的情况下,f[x, y, i] 会产生形状不匹配的错误,因为 a, b, c 的形状不一样。

撰写回答