Numpy 广播机制(两个不同维度对象进行数学运算)

1. 数组相加

一个 2*5 维的数组对象和一个 1 维的数组对象进行相加,结果会怎样?

In [1]: import numpy as np

In [2]: a = np.arange(10).reshape(2,5)

In [3]: a
Out[3]: 
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

In [4]: b = np.array([2])

In [5]: a + b
Out[5]: 
array([[ 2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11]])

In [6]: 

可以看到在维度不同时是将小维度的值全部匹配到大维度的值上,即把 a 中的每个元素都加上 b 的元素。但是看看下面的计算。

In [12]: c = np.array([1,2])

In [13]: c
Out[13]: array([1, 2])

In [14]: a + c
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-14-e81e582b6fa9> in <module>
----> 1 a + c

ValueError: operands could not be broadcast together with shapes (2,5) (2,) 

In [15]: c.shape
Out[15]: (2,)

In [16]: 

为什么 2*5 维的数组和 2 维的数组相加会报错呢? 这是因为 Numpy 的广播机制。因为 a、c 按照广播的规则,无法达成一致的 shape,所以抛出异常。

因为广播机制的存在,b 数组会适配 a 数组,按照第 0、1 维度,分别发生一次广播,广播后的 b 变为:

In [21]: b
Out[21]: array([2])

In [22]: b = np.tile(b,(2,5))

In [23]: b
Out[23]: 
array([[2, 2, 2, 2, 2],
       [2, 2, 2, 2, 2]])

In [24]: 

然后,执行再执行加法操作时,因为 a、b 的 shape 变得完全一致,所以就能实现相加操作了。

2. 广播规则

从上面我们可以看到,不是任意 shape 的多个数组,操作时都能广播到一起,必须满足一定的约束条件。

  • NumPy 首先会比较最靠右的维度,如果最靠右的维度相等或其中一个为 1,则认为此维度相等;
  • 那么,再继续向左比较,如果一直满足,则认为两者兼容;
  • 最后,分别在对应维度上发生广播,以此补齐直到维度一致;

如下,两个数组 a、b,shape 分别为 (2,1,3)、(4,3), 它们能否广播兼容?我们来分析下。

a = np.arange(6).reshape(2,1,3) # shape: (2,1,3)
b = np.arange(12).reshape(4,3) # shape: (4,3)
  • 按照规则,从最右侧维度开始比较,数组 a, b 在此维度上的长度都为 3,相等;
  • 继续向左比较,a 在此维度上长度为 1,b 长度为 4,根据规则,也认为此维度是兼容的;
  • 继续比较,但是数组 b 已到维度终点,停止比较。

结论,数组 a 和 b 兼容,通过广播能实现 shape 一致。

3. 广播步骤

下面看看,数组 a 和 b 广播操作实施的具体步骤。

In [1]: import numpy as np

In [2]: a = np.arange(6).reshape(2,1,3)

In [3]: b = np.arange(12).reshape(4, 3)

In [4]: a
Out[4]: 
array([[[0, 1, 2]],

       [[3, 4, 5]]])

In [5]: b
Out[5]: 
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])

In [6]:

维度编号从 0 开始,数组 a 在维度 1 上发生广播,复制 4 次:

In [6]: a = np.repeat(a, 4, axis=1)

In [7]: a
Out[7]: 
array([[[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2],
        [0, 1, 2]],

       [[3, 4, 5],
        [3, 4, 5],
        [3, 4, 5],
        [3, 4, 5]]])

In [8]: 

此时,数组 a 和 b 在后两个维度一致,但是数组 b 维度缺少一维,所以 b 也会广播一次:

In [8]: b = b[np.newaxis,:,:]	# 首先增加一个维度

In [9]: b
Out[9]: 
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]]])

In [10]: b = np.repeat(b,2,axis=0)	# 在维度 0 上复制 2 次

In [11]: b
Out[11]: 
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]],

       [[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]]])

In [12]: 

经过以上操作,数组 a 和 b 维度都变为 (2,4,3),至此广播完成,做个加法操作:

In [11]: b
Out[11]: 
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]],

       [[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]]])

In [12]: a
Out[12]: 
array([[[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2],
        [0, 1, 2]],

       [[3, 4, 5],
        [3, 4, 5],
        [3, 4, 5],
        [3, 4, 5]]])

In [13]: a + b
Out[13]: 
array([[[ 0,  2,  4],
        [ 3,  5,  7],
        [ 6,  8, 10],
        [ 9, 11, 13]],

       [[ 3,  5,  7],
        [ 6,  8, 10],
        [ 9, 11, 13],
        [12, 14, 16]]])

In [14]: 

验证我们自己实现的广播操作,是否与 NumPy 中的广播操作一致,直接使用原始的 a 和 b 数组相加,看到与上面得到的结果一致。

In [14]: a = np.arange(6).reshape(2,1,3)

In [15]: b = np.arange(12).reshape(4,3)

In [16]: a + b
Out[16]: 
array([[[ 0,  2,  4],
        [ 3,  5,  7],
        [ 6,  8, 10],
        [ 9, 11, 13]],

       [[ 3,  5,  7],
        [ 6,  8, 10],
        [ 9, 11, 13],
        [12, 14, 16]]])

In [17]: 

至此,我们已经了解了广播的规则,所以再遇到如下的广播错误时,我们就可以很清楚的知道该如何解决这样的错误。

ValueError: operands could not be broadcast together with shapes (2,5) (2,) 
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值