您确实可以利用broadcasting 做到这一点。
让我们首先生成一些指定形状的随机ndarrays,以检查最终尺寸是否符合预期:
a = np.random.rand(101, 256, 1, 3, 1, 10)
b = np.random.rand(101)
在这种情况下,您必须将 a.ndim 维度添加到 b,以便将 b 中的每个值减去 a 的最后一个维度中的每个值。按照this 帖子中的想法,我们可以使用np.reshape 以更简洁的方式添加a.ndim 新维度,如下所示:
b = b.reshape((-1,) + (1,)*(a.ndim-1))
print(b.shape)
# (101, 1, 1, 1, 1, 1)
现在我们可以根据需要从a 中减去b:
a[..., 0, None] = a[..., 0, None] - b.reshape((-1,) + (1,) * (a.ndim-1))
如果我们检查a的形状:
print(a.shape)
# (101, 256, 1, 3, 1, 10)
详情
以下是对上一个答案中可能出现的一些问题的一些解释。让我们考虑以下更简单的示例:
a = np.array([[1,2,3],[4,5,6]])
print(a)
array([[1, 2, 3],
[4, 5, 6]])
print(a.shape)
# (2, 3)
b = np.array([1,1])[:,None]
array([[1],
[1]])
print(b.shape)
# (2, 1)
所以对于这个例子,我们可以应用与上述解决方案相同的逻辑:
a[:,0,None] = a[:,0,None] - b
array([[0, 2, 3],
[3, 5, 6]])
通过检查结果数组,正如预期的那样,b 已从沿其最后一个轴的第一个索引上的 a 中减去,因此是所有行中的第一列。
所以第一点,
为什么我们必须在a 中添加一个新轴进行减法?
鉴于b 的形状,有必要向a 添加一个新轴。注意b 是一个二维数组array([[1],[1]]),所以如果你直接从a 中减去它,你会得到:
a[..., 0] - b
array([[0, 3],
[0, 3]])
所以,这里发生的是较小的数组,即第一项,它只是来自a、array([1, 4]) 的 1D 视图切片,已在较大的数组中广播,以便它们兼容形状。
如果b 的形状改为(2,),则不需要这样做:
b = np.array([1,1])
a[:,0] - b
# array([0, 3])
但是由于实际解决方案中b的定义方式,它与a具有相同的维度。所以为了得到正确的输出,我们必须给a添加一个新轴:
a[:,0,None] - b
array([[0],
[3]])
这样我们就得到了正确的输出。
使用上面的方法,似乎无法将差异分配给充当 a 的“更正副本”的新数组?
看一下减法的结果就可以理解这个问题的答案:
c = a[:,0,None] - b
c.shape
(2, 1)
所以这里的a[:,0,None] 是a 的所谓“切片视图”。所以请注意,通过将此结果分配给c,您只保存了a 的实际sliced wiew,而不是整个ndarray。如果要在实际切片的相同位置修改a,则必须将其分配给a 的同一切片视图,因此:
a[:,0,None] = a[:,0,None] - b
print(a.shape)
# (2, 3)
现在结果确实有预期的输出,因为我们只修改了a 的一部分。如果您确实想保存原始ndarray 的副本,您可以使用np.copy,它将返回实际副本而不是a 的一部分,然后将结果分配给“更正后的副本”:
a_c = np.copy(a)
a_c[:,0,None] = a[:,0,None] - b