6. 广播

对不同大小的数组进行计算的时候,需要想法对齐数组的长度,广播就是自动对齐数组长度的一种规则。

6.1. 广播的介绍

广播允许对不同大小的数组进行操作,例如前面介绍的一个标量和一个数组相加,这种自动把自己变成和对方形状一样然后进行 操作的能力,叫广播。

我们先来看几个例子,然后在讲具体的广播规则。

a = np.zeros(5)
print("a = ", a)

# 我们可以认为是 a + 4 = array(0,0,0,0,0) + array(4,4,4,4,4)
# 此时标量4自动扩展成了 array(4,4,4,4,4)
b = a + 4
print("b = ", b)
a =  [0. 0. 0. 0. 0.]
b =  [4. 4. 4. 4. 4.]
a = np.arange(5)
print("a = ", a)
print()

b = np.arange(15).reshape((3,5))
print("b = \n", b)
print()

# 此处相当于把b按行扩展成了一个 3x5的数组,然后和a进行bitwise的相加
c = a + b
print("c = \n", c)
a =  [0 1 2 3 4]

b = 
 [[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]

c = 
 [[ 0  2  4  6  8]
 [ 5  7  9 11 13]
 [10 12 14 16 18]]
# 负责例子,需要两边广播

a = np.arange(3)
b = np.arange(3)[:, np.newaxis]

c = a + b
print("a = \n", a)
print()
print("b = \n", b)
print()
print("c = \n", a + b)
a = 
 [0 1 2]

b = 
 [[0]
 [1]
 [2]]

c = 
 [[0 1 2]
 [1 2 3]
 [2 3 4]]

6.2. 广播的规则

广播必须按照一定规则进行,不能瞎jb播,即便是按照规则,也不是一定能进行广播。

广播规则如下:

  • 规则1: 如果两个数组维度不相同,则小维度数组形状在最左边补上1
  • 规则2: 如果两数组的形状在任何一个维度上都不匹配,则数组的形状会沿着维度为1的维度扩展以匹配零位一个数组
  • 规则3: 如果两两个数组的形状在任何一个维度上都不匹配并且没有任何一个维度等于1, 则异常。
# 规则1 案例

a = np.arange(15).reshape((3,5))
b = np.arange(5)

c = a + b

print("a.shape = ", a.shape)
print("a = \n", a)
print()

print("b.shape = ", b.shape)
print("b = \n", b)
print()

print("c.shape = ", c.shape)
print("c = \n", c)
a.shape =  (3, 5)
a = 
 [[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]

b.shape =  (5,)
b = 
 [0 1 2 3 4]

c.shape =  (3, 5)
c = 
 [[ 0  2  4  6  8]
 [ 5  7  9 11 13]
 [10 12 14 16 18]]

上面例子是规则1的运用案例。


a.shape = (3,5)
b.shape = (5,)

此时如果a, b相加,则按照广播规则1, 需要在b.shape的最左边补上1, 即:

b.shape=(5,) ==> b.shape=(1,5)

然后,可以看作再把b.shape进行第二次扩展:

b.shape(1,5) ==> b.shape(3,5)
# 广播规则2
a = np.arange(5).reshape((5,1))
b = np.arange(5)

c = a + b

print("a.shape = ", a.shape)
print("a = \n", a)
print()

print("b.shape = ", b.shape)
print("b = \n", b)
print()

print("c.shape = ", c.shape)
print("c = \n", c)
a.shape =  (5, 1)
a = 
 [[0]
 [1]
 [2]
 [3]
 [4]]

b.shape =  (5,)
b = 
 [0 1 2 3 4]

c.shape =  (5, 5)
c = 
 [[0 1 2 3 4]
 [1 2 3 4 5]
 [2 3 4 5 6]
 [3 4 5 6 7]
 [4 5 6 7 8]]

上面例子按照规则2进行挂广播。

先考察a, b的shape:

a.shape = (5, 1)
b.shape = (5, )

此时广播需要先对b进行扩展:

b.shape=(5, ) ==> (1,5)

然后跟进a和b的shape分别进行扩展:

a.shape=(5,1) ==> (5,5)
b.shape=(1,5) ==> (5,5)
# 不能广播的案例

a = np.arange(15).reshape((3,5))
b = np.arange(3)

print("a.shape = ", a.shape)
print("b.shape = ", b.shape)

c = a + b
a.shape =  (3, 5)
b.shape =  (3,)



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-95-081747746e13> in <module>()
      7 print("b.shape = ", b.shape)
      8 
----> 9 c = a + b


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

以上案例不能进行广播。

我们考察a和b的shape:

a.shape = (3,5) 
b.shape = (3, )

我们如果需要广播,则按照广播的规则,需要先把b的shape进行补足,则按照规则1:

b.shape = (3,) ==> (1,3)

经过规则1的补齐后,a,b的shap而变成了如下:

a.shape = (3,5)
b.shape = (1,3)

然后按照规则2再次进行匹配后的结果是:

a.shape = (3,5)
b.shape = (3,3)

发现经过匹配后,还是不一致,匹配失败!!!