Numpy 广播切片数组和向量

9 投票
3 回答
2894 浏览
提问于 2025-04-17 20:52

给定三个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时,它们是兼容的,但 yz 在功能上都是 M x 1 的向量,因为一个 M x 0 的向量不包含任何元素。

3 个回答

0

(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 时,ab 会在 axis=1 上对齐,而 a 会在 axis=0 上进行广播:

array([[0, 1, 2, 3],
   [0, 1, 2, 3],
   [0, 1, 2, 3],
   [0, 1, 2, 3]])

但是,如果你想在 axis=0 上对齐 ab,并在 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 上进行广播呢?)

0

使用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
4

一般来说,广播的规则是会在数组的形状前面插入单一维度。这让我们在处理数组的最后几维时更加方便,所以 (x.T - z).T 这样的操作是可以正常工作的。

如果系统自动决定 x 的哪个轴与 z 匹配的话,那么像 x - z 这样的操作就会在 N == M 的情况下出错,这样会让代码的测试变得更加困难。因此,这个规则在提供便利的同时,也能减少一些错误的发生。

如果你不喜欢 z[:, None] 这种简写方式,或许你会觉得 z[:, np.newaxis] 更加清晰。

对于像 x[:,0] = y 这样的赋值操作,如果想让它正常工作,你可以用 x[:,0:1] = y 来替代。

撰写回答