本系列是《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. 维度相同 → 直接运算
  2. 某个维度是 1 → 广播到和另一个相同
  3. 维度不匹配且都不是 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 且不相等 → 报错

一个实用的思考顺序:

  1. 看最后一个维度——想"逐元素"还是"逐行/逐列"
  2. 需要时用 [:, 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

广播是零拷贝的,数据量越大优势越明显。始终优先用广播。

常见报错

V a l u e E r r o r : o p e r a n d s c o u l d n o t b e b r o a d c a s t t o g e t h e r w i t h s h a p e s ( 3 , 2 ) ( 4 , 2 )

看到这个错,就用广播规则核对两个数组的 shape,从后往前逐维比对:

  1. 末尾两个维度相等吗?→ 不相等(2 vs 4)
  2. 有维度是 1 吗?→ 都不是 1 → 所以报错。修法:改 shape 或升维,让它们能对上。

小结

场景 形状 结果
标量广播 (n,) + 标量 (n,)
每行加同数 (m,n) + (n,) (m,n)
每列加同数 (m,n) + (m,1) (m,n)
两方向都广播 (m,1) + (1,n) (m,n)
  1. 规则:末尾对齐、维度为 1 则广播、都不为 1 且不等则报错
  2. 广播零拷贝,性能好;方向不对用 [:, np.newaxis] 控制
  3. 报错时从最后一个维度往前排查

下一章:用布尔条件筛选数据(布尔索引)。


上一篇:第 7 章 统计与聚合 | 下一篇:第 9 章 布尔索引与筛选


上一篇:第 7 章 统计与聚合 | 下一篇:第 9 章 布尔索引与筛选