第 7 章 广播
学习目标
- 理解广播(broadcasting)的定义
- 掌握广播的三条规则
- 能判断两个数组能否广播
- 会利用广播简化代码:z-score 标准化、行/列运算
7.1 什么是广播
广播(broadcasting)是 NumPy 在形状不同的数组之间做逐元素运算时,自动「拉伸」较小数组以匹配较大数组的机制。
你已经见过最简单的广播:数组与标量相加。
import numpy as np
a = np.array([1, 2, 3])
print(a + 10) # 10 被广播到每个元素输出:
[11 12 13]标量是 0 维的「数组」。NumPy 把 10 拉伸成与 a 相同形状,逐元素相加。
7.2 广播的三种常见形态
形态一:标量与数组
标量 + 数组,标量广播到整个数组(上面的例子)。
形态二:行向量与二维数组
(m, n) 数组 + (n,) 数组,一维数组沿行方向广播到每一行:
import numpy as np
m = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
row = np.array([10, 20, 30])
print(m + row)输出:
[[11 22 33]
[14 25 36]
[17 28 39]]row 长度 3 与 m 的列数 3 匹配,广播成 3×3 后相加。这是「给每一行加上同一个向量」。
形态三:列向量与二维数组
(m, n) 数组 + (m, 1) 数组,列向量沿列方向广播到每一列:
import numpy as np
m = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
col = np.array([[1], [2], [3]]) # (3, 1)
print(m + col)输出:
[[ 2 3 4]
[ 6 7 8]
[10 11 12]]col 是 3 行 1 列,先水平拉伸成 3×3,再与 m 相加:第 i 行整体加上 col[i, 0]。
7.3 广播规则
判断两个形状能否广播,规则只有两条:
- 从右往左对齐维度,逐个比较。
- 每一对维度,要么相等,要么其中一个是 1(会被拉伸到另一个),要么缺失(视为 1)。
例子:形状 (3,) 与 (3, 1) 相加:
(3,) → 对齐为 (1, 3)
(3, 1) → (3, 1)
逐位比较:1 vs 3 → 拉伸;3 vs 1 → 拉伸
结果 (3, 3)import numpy as np
v = np.array([1, 2, 3])
print(v + v[:, np.newaxis]) # (3,) + (3, 1) → (3, 3)输出:
[[2 3 4]
[3 4 5]
[4 5 6]]不满足规则时,报 ValueError。两个典型的失败例子:
>>> np.array([1, 2, 3]) + np.array([1, 2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (3,) (2,)
>>> np.ones((3, 2)) + np.ones((2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (3,2) (2,3)第一个:3 与 2 既不相等、也没有 1,无法拉伸。第二个:对齐后 (3, 2) 与 (2, 3),第 0 位 3 vs 2、第 1 位 2 vs 3,都不匹配。报错信息里的 shapes (...) 直接给出了两个形状,对照规则即可判断。
规则总结表:
| 形状 A | 形状 B | 结果 | 说明 |
|---|---|---|---|
(n,) | () 标量 | (n,) | 标量广播 |
(m, n) | (n,) | (m, n) | 行向量广播到每行 |
(m, n) | (m, 1) | (m, n) | 列向量广播到每列 |
(n,) | (n, 1) | (n, n) | 互相拉伸成方阵 |
(3,) | (2,) | 报错 | 3 vs 2,不匹配 |
(3, 2) | (2, 3) | 报错 | 两个维度都不匹配 |
7.4 应用:z-score 标准化
z-score 标准化把每列数据减去列均值、再除以列标准差,使每列均值为 0、标准差为 1。用广播一行搞定:
import numpy as np
data = np.array([[170, 65], # 身高,体重
[175, 70],
[168, 55],
[182, 80]])
mean = data.mean(axis=0) # 每列均值,形状 (2,)
std = data.std(axis=0) # 每列标准差,形状 (2,)
z = (data - mean) / std # 广播:每列减去自己的均值、除以自己的标准差
print(z)输出:
[[-0.69411722 -0.2773501 ]
[ 0.23137241 0.2773501 ]
[-1.06431307 -1.38675049]
[ 1.52705788 1.38675049]]解读:data 是 (4, 2),mean 是 (2,)。减法时 mean 沿行广播到每一行,第 0 列减身高均值、第 1 列减体重均值——列与列互不干扰。没有广播的话,你要写两层循环。
7.5 利用列向量广播
把形状不对齐的数据先变成列向量,再广播。例如给每行加一个「行号偏移」:
import numpy as np
m = np.ones((2, 3))
offset = np.array([[1], [2]]) # (2, 1)
print(m + offset)输出:
[[2. 2. 2.]
[3. 3. 3.]]动手实践
- 手动判断以下组合能否广播,能的话写出结果形状:
(4, 3)与(3,);(4, 3)与(4, 1);(4, 3)与(3, 4);(5, 1)与(1, 4)。用代码验证。 - 创建 4×4 数组,给每一行加上
[10, 20, 30, 40];再给每一列加上[[1], [2], [3], [4]]。 - 用广播对成绩表
np.array([[85, 90, 78], [92, 88, 95], [76, 84, 80]])做 z-score 标准化,验证每列均值约为 0。 - 构造一个「不能广播」的组合,运行并读出报错里的两个形状。
常见错误
| 错误写法 | 现象 | 原因 |
|---|---|---|
(3,) 与 (2,) 相加 | ValueError: operands could not be broadcast together with shapes (3,) (2,) | 对应维度 3 vs 2,都不为 1,无法广播 |
(3, 2) 与 (2, 3) 相加 | 同上 | 两个维度都不匹配 |
忘了给列向量加 newaxis | 形状报错或结果不对 | 需要列广播时先 v[:, np.newaxis] 变成 (n, 1) |
| 以为广播会「补零」 | 报错而不是自动补零 | 广播只拉伸长度为 1 的维度,不补数据 |
| 广播结果出乎意料 | 结果数组形状是两者形状的最大值 | 先按规则推结果形状,再验证 |
章末练习
基础
- 判断并写出结果形状:
(5,) + (5, 1)、(2, 3) + (3,)、(4, 2) + (4, 1)。 - 用广播给 3×3 数组
np.arange(9).reshape(3, 3)的每一行加上[1, 2, 3]。 - 给同一数组的每一列加上
[[10], [20], [30]],打印结果。
提高
- 生成 5 名学生的 3 门课成绩(自行设计),计算 z-score 标准化矩阵,并验证每列
mean约为 0、std约为 1。 - 用广播把摄氏温度数组
np.array([0, 10, 20, 30, 40])转成华氏,再与np.array([[0], [10], [20]])组合演示一个二维广播例子。
挑战
- 解释为什么
np.arange(3) + np.arange(3)[:, np.newaxis]得到 3×3 的加法表,写出结果。 - 用广播写一个函数
normalize_rows(a),把每行归一化到和为 1(提示:每行除以行和,行和用sum(axis=1, keepdims=True))。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
- 广播的定义是?
- A. 把数组变成列表
- B. 自动拉伸较小数组以匹配较大数组做逐元素运算
- C. 复制数组
- D. 打印数组
np.array([1, 2, 3]) + 5依赖的机制是?- A. 类型转换
- B. 广播
- C. 拼接
- D. 循环
- 形状
(3,)与(3, 1)广播的结果形状是?- A.
(3,) - B.
(3, 1) - C.
(3, 3) - D. 报错
- A.
- 形状
(4, 3)与(3,)广播的结果形状是?- A.
(4,) - B.
(4, 3) - C.
(3, 4) - D. 报错
- A.
- 形状
(3,)与(2,)相加会?- A. 结果
(5,) - B. 结果
(3, 2) - C. 报
ValueError - D. 结果
(2,)
- A. 结果
- 需要列向量广播时,应把
v变成?- A.
v.T - B.
v[:, np.newaxis] - C.
v.reshape(-1) - D.
v.flatten()
- A.
- z-score 标准化的公式是?
- A.
(x - min) / (max - min) - B.
(x - mean) / std - C.
x / sum - D.
(x - mean) / n
- A.
- 广播规则中,一对维度能拉伸的条件是?
- A. 两者相等或其中一个为 1
- B. 两者都是偶数
- C. 两者都大于 1
- D. 任意都能拉伸
- 下列哪组形状不能广播?
- A.
(2, 3)与(3,) - B.
(2, 3)与(2, 1) - C.
(2, 3)与(3, 2) - D.
(3,)与(1, 3)
- A.
np.arange(3) + np.arange(3)[:, np.newaxis]的结果形状是?- A.
(3,) - B.
(3, 3) - C.
(1, 3) - D. 报错
- A.
