Numpy 广播切片数组和向量
给定三个numpy数组:一个多维数组 x
,一个带有单一维度的向量 y
,还有一个没有单一维度的向量 z
。
x = np.zeros((M,N))
y = np.zeros((M,1))
z = np.zeros((M,))
在进行广播操作时,向量的表示方式和上下文会影响结果:
x[:,0] = y # error cannot broadcast from shape (M,1) into shape (M)
x[:,0] = z # OK
x[:,0] += y # error non-broadcastable output with shape (M) doesn't match
# broadcast shape(M,M)
x[:,0] += z # OK
x - y # OK
x - z # error cannot broadcast from shape (M,N) into shape (M)
我意识到我可以这样做:
x - z[:,None] # OK
但我不明白这种明确的表示法有什么好处。它显然没有提高可读性。我不明白为什么表达式 x - y
是可以的,而 x - z
却有歧义。
为什么Numpy对带有或不带有单一维度的向量的处理方式不同?
补充:文档中提到:当两个维度相等,或者其中一个是1时,它们是兼容的,但 y
和 z
在功能上都是 M x 1
的向量,因为一个 M x 0
的向量不包含任何元素。
3 个回答
将 (M,1)
和 (M,)
视为不同的一个好处是,你可以指定哪些维度要对齐,哪些维度要进行广播。
假设你有:
a = np.arange(4)
b = np.arange(16).reshape(4,4)
# i.e a = array([0, 1, 2, 3])
# i.e b = array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15]])
当你执行 c = a + b
时,a
和 b
会在 axis=1
上对齐,而 a
会在 axis=0
上进行广播:
array([[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3]])
但是,如果你想在 axis=0
上对齐 a
和 b
,并在 axis=1
上进行广播呢?
array([[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
将 (M,1)
和 (M,)
区分开来,可以让你指定哪个维度要对齐,哪个维度要广播。
(也就是说,如果 (M,1)
和 (M,)
被视为相同,怎么告诉 numpy 你想在 axis=1
上进行广播呢?)
使用Numpy的矩阵接口,而不是数组接口,可以得到想要的广播行为:
x = np.asmatrix(np.zeros((M,N)))
y = np.asmatrix(np.zeros((M,1)))
x[:,0] = y # OK
x[:,0] = y[:,0] # OK
x[:,0] = y[:,0:1] # OK
x[:,0] += y # OK
x - y # OK
x - np.mean(x, axis=0) # OK
x - np.mean(x, axis=1) # OK
一般来说,广播的规则是会在数组的形状前面插入单一维度。这让我们在处理数组的最后几维时更加方便,所以 (x.T - z).T
这样的操作是可以正常工作的。
如果系统自动决定 x
的哪个轴与 z
匹配的话,那么像 x - z
这样的操作就会在 N == M
的情况下出错,这样会让代码的测试变得更加困难。因此,这个规则在提供便利的同时,也能减少一些错误的发生。
如果你不喜欢 z[:, None]
这种简写方式,或许你会觉得 z[:, np.newaxis]
更加清晰。
对于像 x[:,0] = y
这样的赋值操作,如果想让它正常工作,你可以用 x[:,0:1] = y
来替代。