NumPy 广播
本章目标
- 掌握广播(broadcasting)的三条核心规则
- 掌握标量、行向量、列向量与矩阵的广播计算
- 学会用广播实现外积
- 掌握广播在数据标准化中的典型应用
重点方法与概念速览
| 名称 | 类型 | 作用 |
|---|---|---|
arr + scalar | 表达式 | 标量与数组广播(逐元素运算) |
arr2d + arr1d | 表达式 | 一维向量沿行方向广播 |
arr2d + col | 表达式 | 列向量 (n, 1) 沿列方向广播 |
a[:, np.newaxis] * b | 表达式 | 用广播手动构造外积 |
np.outer(...) | 函数 | 外积(一维向量专用 API) |
arr.mean(axis=0) | 方法 | 按列均值,配合广播用于标准化 |
arr.std(axis=0) | 方法 | 按列标准差,配合广播用于标准化 |
1. 广播规则
三条核心规则
- 从后往前(末尾对齐)逐维比较两个数组的
shape - 每一维满足相等或其中一个为 1即可广播
- 缺失的维度按
1处理,自动在前方补上
形状兼容速查
| 左形状 | 右形状 | 是否兼容 | 广播后形状 |
|---|---|---|---|
(3, 4) | (4,) | 是 | (3, 4) |
(3, 4) | (3, 1) | 是 | (3, 4) |
(3, 1) | (1, 4) | 是 | (3, 4) |
(2, 3, 4) | (3, 4) | 是 | (2, 3, 4) |
(3, 4) | (3,) | 否 | — |
(3, 4) | (2, 4) | 否 | — |
示例代码
python
import numpy as np
A = np.ones((3, 4))
B1 = np.array([1, 2, 3, 4]) # (4,)
B2 = np.array([[1], [2], [3]]) # (3, 1)
print(f"(3,4) + (4,) = {(A + B1).shape}")
print(f"(3,4) + (3,1) = {(A + B2).shape}")
print(f"(3,1) + (1,4) = "
f"{(np.ones((3, 1)) + np.ones((1, 4))).shape}")输出
text
(3,4) + (4,) = (3, 4)
(3,4) + (3,1) = (3, 4)
(3,1) + (1,4) = (3, 4)2. 标量与数组广播
作用
标量(0 维)与任意形状的数组均可广播,标量自动扩展为同形状后逐元素运算。
示例代码
python
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(f"arr + 10:\n{arr + 10}")
print(f"arr * 2:\n{arr * 2}")输出
text
arr + 10:
[[11 12 13]
[14 15 16]
[17 18 19]]
arr * 2:
[[ 2 4 6]
[ 8 10 12]
[14 16 18]]3. 一维数组沿行广播
作用
arr2d (m, n) 与 arr1d (n,) 相加时,一维数组被视作 (1, n) 后在行方向复制 m 份,相当于"每一行加同一个向量"。
示例代码
python
import numpy as np
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr1d = np.array([10, 20, 30])
print(f"arr2d + arr1d:\n{arr2d + arr1d}")输出
text
arr2d + arr1d:
[[11 22 33]
[14 25 36]
[17 28 39]]理解重点
(m, n)与(n,)对齐后,第 0 轴长度自动补为1,再广播到m- 若要"每一列加同一个向量",需要形状
(m, 1)而不是(m,)
4. 列向量沿列广播
作用
arr2d (m, n) 与 col (m, 1) 相加时,列向量在列方向复制 n 份,相当于"每一列加同一个列向量"。
示例代码
python
import numpy as np
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
col = np.array([[100], [200], [300]]) # (3, 1)
print(f"arr2d + col:\n{arr2d + col}")输出
text
arr2d + col:
[[101 102 103]
[204 205 206]
[307 308 309]]理解重点
- 想要"列向量"必须显式做成
(m, 1):可用arr.reshape(-1, 1)、arr[:, np.newaxis]或np.expand_dims(arr, axis=1) (m,)与(m, 1)在广播中方向不同:前者沿行方向广播,后者沿列方向广播
5. 广播应用:外积
外积 (len(a), len(b)),元素公式:
用广播的视角:把 (m, 1),(n,),相乘即得。
np.outer
作用
直接计算两个一维向量的外积,等价于 a[:, np.newaxis] * b。
重点方法
python
np.outer(a, b, out=None)参数
| 参数名 | 类型 | 说明 | 示例取值 |
|---|---|---|---|
a | array_like | 第一个输入,会被展平为一维 | [1, 2, 3] |
b | array_like | 第二个输入,会被展平为一维 | [10, 20, 30, 40] |
out | ndarray 或 None | 写入结果的目标数组 | —— |
综合示例
示例代码
python
import numpy as np
a = np.array([1, 2, 3])
b = np.array([10, 20, 30, 40])
# 方式 1:广播
print(f"广播外积:\n{a[:, np.newaxis] * b}")
# 方式 2:np.outer
print(f"np.outer:\n{np.outer(a, b)}")输出
text
广播外积:
[[ 10 20 30 40]
[ 20 40 60 80]
[ 30 60 90 120]]
np.outer:
[[ 10 20 30 40]
[ 20 40 60 80]
[ 30 60 90 120]]6. 广播应用:特征标准化
Z-score 标准化公式:
其中 axis=0)计算。它们形状为 (n_features,),与数据 (n_samples, n_features) 相减/相除时自动沿行广播。
示例代码
python
import numpy as np
np.random.seed(42)
data = np.random.randint(0, 100, size=(5, 3))
print(f"原始数据:\n{data}")
mean = data.mean(axis=0) # (3,)
std = data.std(axis=0) # (3,)
normalized = (data - mean) / std
print(f"每列均值: {mean.round(2)}")
print(f"每列标准差: {std.round(2)}")
print(f"标准化后:\n{normalized.round(2)}")
print(f"标准化后均值: {normalized.mean(axis=0).round(10)}")
print(f"标准化后标准差: {normalized.std(axis=0).round(2)}")输出
text
原始数据:
[[51 92 14]
[71 60 20]
[82 86 74]
[74 87 99]
[23 2 21]]
每列均值: [60.2 65.4 45.6]
每列标准差: [21.22 33.61 34.4 ]
标准化后:
[[-0.43 0.79 -0.92]
[ 0.51 -0.16 -0.74]
[ 1.03 0.61 0.83]
[ 0.65 0.64 1.55]
[-1.75 -1.89 -0.72]]
标准化后均值: [-0. -0. -0.]
标准化后标准差: [1. 1. 1.]理解重点
mean/std形状(3,),与data (5, 3)运算时自动沿行广播到(5, 3)- 标准化后每列均值为 0、标准差为 1——这正是
StandardScaler的底层实现 - 用
keepdims=True得(1, 3)形状,语义更明确,对高维也通用
常见坑
- 维度不兼容时 NumPy 抛
ValueError: operands could not be broadcast together——先打印各自shape定位 (n,)、(n, 1)、(1, n)在广播中语义完全不同,不要凭直觉写- 广播本身不复制数据(内部用 stride trick),但运算结果会分配新内存
- 标准化时分母
std可能为 0(常数列),结果出现inf/nan,生产代码应加epsilon - 高维张量建议始终用
keepdims=True聚合,结果形状更可控
小结
- 广播是 NumPy 高性能表达力的核心,消除了大量显式循环
- 记住三条规则(后向对齐、为 1 可扩、缺失补 1),绝大多数场景都能自洽
- 主动使用
np.newaxis/expand_dims控制广播方向,比依赖隐式(n,)更清晰