添加维度不同的数组

13 投票
2 回答
17863 浏览
提问于 2025-04-15 18:16

假设我有一个二维的Numpy数组:

>>> a = np.random.random((4,6))

我想把一个一维数组加到每一行:

>>> c = np.random.random((6,))
>>> a + c

这样做是可以的。但是如果我想把一个一维数组加到每一列,就会出错:

>>> b = np.random.random((4,))
>>> a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: shape mismatch: objects cannot be broadcast to a single shape

我可以通过使用 np.newaxis 来解决这个问题:

>>> a + b[:,np.newaxis]

这样就能按预期工作了。

那么,怎样的形状匹配规则可以避免使用np.newaxis呢?是不是说Numpy数组形状的最后一个元素必须匹配?这个规则在更高维度的情况下也适用吗?比如,下面这个是可以的:

>>> a = np.random.random((2,3,4,5))
>>> b = np.random.random((4,5))
>>> a + b

所以我的问题是,这些规则有没有地方可以查到?这种行为是否可靠,还是说最好总是使用np.newaxis?

2 个回答

1

让我看看我是否理解了...

>>> from numpy import ones, newaxis
>>> A = ones((4,3))   # 4 rows x 3 cols
>>> A.shape
(4, 3)
>>> A
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
>>> 
>>> ones((4,1))      # 4 rows x 1 col
array([[ 1.],
       [ 1.],
       [ 1.],
       [ 1.]])
>>> A + ones((4,1))
array([[ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.]])
>>> 
>>> ones((1,3))      # 1 row x 3 cols
array([[ 1.,  1.,  1.]])
>>> A + ones((1,3))  
array([[ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.]])
>>> 
>>> B = ones((3,))   # a 1D array
>>> B
array([ 1.,  1.,  1.])
>>> B.shape
(3,)
>>> A + B
array([[ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.]])
>>> 
>>> C = ones((4,))   # a 1D array
>>> C.shape
(4,)
>>> C
array([ 1.,  1.,  1.,  1.])
>>> A + C
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: shape mismatch: objects cannot be broadcast to a single shape
>>> 
>>> D = C[:,newaxis]
>>> D.shape
(4, 1)
>>> A + D
array([[ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.]])

当我们要把一个4行3列的矩阵和一个有3个元素的一维数组相加时,这个操作是成功的。

但是,如果我们想把同样的4行3列的矩阵和一个有4个元素的一维数组相加,那就失败了。

>>> D = C[:,newaxis]

这个操作会把C转换成一个形状合适的二维矩阵。

14

这是numpy的一个独特功能,叫做“广播”: 如果你可以把一个向量和一个标量相乘,那为什么不可以把一个矩阵和一个向量相乘呢?就像在第一个情况下,向量的每个元素都和标量相乘一样,在第二种情况下,矩阵每一行的每个单元格都和对应的向量元素相乘。

广播的实现有四条规则,虽然这些规则的表述有点复杂,但一旦理解后其实很直观:

  1. 所有维度ndim小于最大ndim的输入数组,其形状前面会加上1。
  2. 输出形状每个维度的大小是所有输入在该维度的大小中的最大值。
  3. 如果某个输入在特定维度的大小要么和输出在该维度的大小相同,要么大小为1,那么这个输入就可以参与计算。
  4. 如果某个输入在其形状的某个维度的大小为1,那么在该维度的所有计算中都会使用该维度的第一个数据项。换句话说,ufunc的计算不会在这个维度上移动(该维度的步长为0)。

例如,

enter image description here

在以下三种情况下,这种操作是可能的(不会出现你提到的shape mismatch错误):

  1. 所有数组的形状完全相同。
  2. 所有数组的维度数量相同,并且每个维度的长度要么是相同的长度,要么是1。
  3. 维度过少的数组可以在其形状前面加上一个长度为1的维度,以满足第二条规则。

更多的例子可以在我最近的关于广播的文章中找到 [1] 或者在官方文档中 [2]。

参考资料

  1. NumPy中的广播,2021 https://towardsdatascience.com/58856f926d73
  2. NumPy参考。广播 http://docs.scipy.org/doc/numpy/reference/ufuncs.html#broadcasting

撰写回答