本系列是《NumPy 速通》的配套章节。
广播(Broadcasting)是 NumPy 最强大的特性,也是最难理解的。简单说:当两个形状不同的数组做运算时,NumPy 会自动把小的那个"扩展"到大数组的形状,然后逐元素运算——而且不占用额外内存。
类比:就像老师宣布"每人加 5 分",这个"5"会自动广播到每个学生的成绩上,不需要手动复制 5 很多遍。广播 = 写更少的代码 + 不复制数据(省内存)。
标量广播(最简单的广播)
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
arr + 10 # [11 12 13 14 15]
原理:10 被"广播"成 [10, 10, 10, 10, 10],然后逐元素相加。这个展开是 NumPy 内部虚拟的,不占额外内存——所以它比 np.tile 或列表复制快得多、省得多。
数组与数组的广播
arr = np.array([[1, 2, 3],
[4, 5, 6]]) # shape (2, 3)
col = np.array([10, 20, 30]) # shape (3,)
arr + col # (2,3) 和 (3,) → 广播成两行都加
# [[11 22 33]
# [14 25 36]]
(2,3) 和 (3,) 能直接相加——(3,) 从最后一个维度对齐,被广播成了两行。
⚠️ 灵魂警告:对齐是从最后一个维度开始的!
row = np.array([[10], [20]]) # shape (2, 1),想当"每列"用 arr + row # ❌ (2,3) + (2,1) → 按列加,不是按行! # 结果 (2, 3) 但每一列都加了 [10,20]想"每行加同样的数"→ 用
(3,);想"每列加不同的数"→ 用(3,1)(见下)。
⭐ 广播规则(务必背下来)
NumPy 从最后一个维度开始往前比较两个数组的 shape:
- 维度相同 → 直接运算
- 某个维度是 1 → 广播到和另一个相同
- 维度不匹配且都不是 1 → 报错
# —— 合法案例 ——
a = np.zeros((3, 1)) # 3 行 1 列
b = np.zeros((1, 4)) # 1 行 4 列
a + b # 结果 (3, 4),两个 1 都被扩展
# —— 非法案例 ——
a = np.zeros((3, 2))
b = np.zeros((4, 2))
a + b # ValueError: operands could not be broadcast
逐维对齐的直观演示
a.shape = (2, 3) # 2 3
b.shape = (3,) # 3 ← 末尾对齐
# 2 3 两个维度分别相等 ✅
a.shape = (2, 3) # 2 3
b.shape = (1, 3) # 1 3 ← 末尾对齐
# 2 3 第二维相同;第一维 1 → 广播 ✅
a.shape = (3, 2) # 3 2
b.shape = (4, 2) # 4 2 ← 末尾对齐
# 3 4 ❌ 都不为 1 且不相等 → 报错
一个实用的思考顺序:
- 看最后一个维度——想"逐元素"还是"逐行/逐列"
- 需要时用
[:, np.newaxis](第 5 章)主动升维,让广播朝想要的方向走
实战:把一维数组"变列"再广播(最常用技巧)
想给每一列加不同的数:
arr = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]) # (3, 3)
col = np.array([100, 200, 300]) # (3,)
arr + col # ❌ 这是"每行加 [100,200,300]"
# [[101 202 303]
# [104 205 306]
# [107 208 309]]
arr + col[:, np.newaxis] # ✅ 升维成 (3,1) → 每列加不同值
# [[101 102 103]
# [204 205 206]
# [307 308 309]]
[:, np.newaxis] 是广播的"方向盘"——它把一维数组变成列向量,控制广播方向。
实战:标准化(去均值 + 归一化)
广播最常见的用途——给每一列减去该列均值:
data = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]) # (3, 3)
col_mean = data.mean(axis=0) # [4. 5. 6.] 每列均值 (3,)
centered = data - col_mean # 广播减法
# [[-3. -3. -3.]
# [ 0. 0. 0.]
# [ 3. 3. 3.]]
# 进一步标准化(除以每列标准差)
col_std = data.std(axis=0) # [2.449 2.449 2.449]
standardized = (data - col_mean) / col_std
一行代码完成整列归一化,正是广播的威力。机器学习特征工程里这一步天天用。
广播 vs 显式复制(性能对比)
# 方法一:广播(推荐)—— 不复制数据
arr + col
# 方法二:手动复制(别这么做)—— 浪费内存
np.tile(col, (3, 1)) + arr
广播是零拷贝的,数据量越大优势越明显。始终优先用广播。
常见报错
看到这个错,就用广播规则核对两个数组的 shape,从后往前逐维比对:
- 末尾两个维度相等吗?→ 不相等(2 vs 4)
- 有维度是 1 吗?→ 都不是 1 → 所以报错。修法:改 shape 或升维,让它们能对上。
小结
| 场景 | 形状 | 结果 |
|---|---|---|
| 标量广播 | (n,) + 标量 |
(n,) |
| 每行加同数 | (m,n) + (n,) |
(m,n) |
| 每列加同数 | (m,n) + (m,1) |
(m,n) |
| 两方向都广播 | (m,1) + (1,n) |
(m,n) |
- 规则:末尾对齐、维度为 1 则广播、都不为 1 且不等则报错
- 广播零拷贝,性能好;方向不对用
[:, np.newaxis]控制 - 报错时从最后一个维度往前排查
下一章:用布尔条件筛选数据(布尔索引)。
上一篇:第 7 章 统计与聚合 | 下一篇:第 9 章 布尔索引与筛选
上一篇:第 7 章 统计与聚合 | 下一篇:第 9 章 布尔索引与筛选