Numpy 错误:形状不匹配
我在用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 个回答
在你的问题中,不太清楚你想要做什么或者你期待的结果是什么。不过,看起来你是想用你的变量 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)
我们来把你的问题简化成二维的情况,这样更容易理解:
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])
看起来你想对最后一个维度的 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)
ogrid
和 mgrid
作为 meshgrid
的替代品。
我们来看一个和你的 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
的形状不一样。