## 9.7 数组上的计算：广播

### 广播简介

``````import numpy as np

a = np.array([0, 1, 2])
b = np.array([5, 5, 5])
a + b

# array([5, 6, 7])
``````

``````a + 5

# array([5, 6, 7])
``````

NumPy 广播的优势在于，这种值的重复实际上并没有发生，但是当我们考虑广播时，它是一种有用的心理模型。

``````M = np.ones((3, 3))
M

'''
array([[ 1.,  1.,  1.],
[ 1.,  1.,  1.],
[ 1.,  1.,  1.]])
'''

M + a

'''
array([[ 1.,  2.,  3.],
[ 1.,  2.,  3.],
[ 1.,  2.,  3.]])
'''
``````

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

print(a)
print(b)

'''
[0 1 2]
[[0]
[1]
[2]]
'''

a + b

'''
array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])
'''
``````

### 广播规则

NumPy 中的广播遵循一套严格的规则来确定两个数组之间的交互：

• 规则 1：如果两个数组的维数不同，则维数较少的数组的形状，将在其左侧填充。
• 规则 2：如果两个数组的形状在任何维度上都不匹配，则该维度中形状等于 1 的数组将被拉伸来匹配其他形状。
• 规则 3：如果在任何维度中，大小不一致且都不等于 1，则会引发错误。

#### 广播示例 1

``````M = np.ones((2, 3))
a = np.arange(3)
``````

• `M.shape = (2, 3)`
• `a.shape = (3,)`

• `M.shape -> (2, 3)`
• `a.shape -> (1, 3)`

• `M.shape -> (2, 3)`
• `a.shape -> (2, 3)`

``````M + a

'''
array([[ 1.,  2.,  3.],
[ 1.,  2.,  3.]])
'''
``````

#### 广播示例 2

``````a = np.arange(3).reshape((3, 1))
b = np.arange(3)
``````

• `a.shape = (3, 1)`
• `b.shape = (3,)`

• `a.shape -> (3, 1)`
• `b.shape -> (1, 3)`

• `a.shape -> (3, 3)`
• `b.shape -> (3, 3)`

``````a + b

'''
array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])
'''
``````

#### 广播示例 3

``````M = np.ones((3, 2))
a = np.arange(3)
``````

• `M.shape = (3, 2)`
• `a.shape = (3,)`

• `M.shape -> (3, 2)`
• `a.shape -> (1, 3)`

• `M.shape -> (3, 2)`
• `a.shape -> (3, 3)`

``````M + a

'''
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-13-9e16e9f98da6> in <module>()
----> 1 M + a

ValueError: operands could not be broadcast together with shapes (3,2) (3,)
'''
``````

``````a[:, np.newaxis].shape

# (3, 1)

M + a[:, np.newaxis]

'''
array([[ 1.,  1.],
[ 2.,  2.],
[ 3.,  3.]])
'''
``````

``````np.logaddexp(M, a[:, np.newaxis])

'''
array([[ 1.31326169,  1.31326169],
[ 1.69314718,  1.69314718],
[ 2.31326169,  2.31326169]])
'''
``````

### 实战中的广播

#### 数组中心化

``````X = np.random.random((10, 3))
``````

``````Xmean = X.mean(0)
Xmean

# array([ 0.53514715,  0.66567217,  0.44385899])
``````

``````X_centered = X - Xmean
``````

``````X_centered.mean(0)

# array([  2.22044605e-17,  -7.77156117e-17,  -1.66533454e-17])
``````

#### 绘制二维函数

``````# x 和 y 是从 0 到 5 的 50 步
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 50)[:, np.newaxis]

z = np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
``````

``````%matplotlib inline
import matplotlib.pyplot as plt

plt.imshow(z, origin='lower', extent=[0, 5, 0, 5],
cmap='viridis')
plt.colorbar();
``````