本文介绍如何使用 NumPy 的 sliding_window_view 与索引查表技术,对二维数组每列中垂直相邻元素组成的有序对,快速查表替换为预定义规则值,全程避免 Python 循环,兼顾密集与稀疏场景的性能优化。
本文介绍如何使用 numpy 的 `sliding_window_view` 与索引查表技术,对二维数组每列中垂直相邻元素组成的有序对,快速查表替换为预定义规则值,全程避免 python 循环,兼顾密集与稀疏场景的性能优化。
在科学计算和数据处理中,常需将数组中连续行构成的“状态转移对”(如 (a[i, j], a[i+1, j]))依据外部规则映射为标量值。本例中,输入数组 a 的每一列代表一条离散序列,而规则数组 b 定义了所有可能的有序对 (x, y) 到非负整数的映射。目标是生成输出数组 c,其第 j 列包含 a 第 j 列中所有相邻行对(共 len(a)-1 对)对应的映射结果。
核心挑战在于:既要保证逻辑正确性(顺序敏感、逐列独立),又要满足高频调用下的性能要求。最优解是向量化查表——将规则矩阵化,再通过滑动窗口提取所有相邻对,最后一次性索引完成映射。
✅ 密集索引场景:基于二维查找表的极速方案
当 b 中的取值范围紧凑(例如仅含 1, 2, 3 或 0~N-1),推荐构建稠密二维查找表 s:
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view as swv
# 示例数据
a = np.array([[2, 3, 1, 3],
[3, 2, 1, 3],
[1, 1, 1, 2],
[1, 3, 2, 3],
[3, 3, 1, 3],
[2, 1, 3, 2]])
b = np.array([[1, 1, 6],
[1, 2, 0],
[1, 3, 9],
[2, 1, 6],
[2, 2, 0],
[2, 3, 4],
[3, 1, 1],
[3, 2, 0],
[3, 3, 8]])
# 构建查找表 s[x, y] = value;支持 1-based 索引(更直观)
max_val = b[:, :2].max()
s = np.full((max_val + 1, max_val + 1), -1) # 初始化为 -1(非法值占位符)
s[b[:, 0], b[:, 1]] = b[:, 2]
# 提取所有垂直相邻对:shape (n-1, cols, 2)
pairs = swv(a, 2, axis=0) # 沿行方向滑动,窗口大小为 2
# 向量化索引:s[pairs[...,0], pairs[...,1]]
c = s[pairs[..., 0], pairs[..., 1]]
print(c)
# 输出:
# [[4 0 6 8]
# [1 6 6 0]
# [6 9 0 4]
# [9 8 6 8]
# [0 1 9 0]]
? 关键点说明:
- sliding_window_view(a, 2, axis=0) 将 a(形状 (6, 4))转换为 (5, 4, 2),每个 (i,j,:) 即 a[i,j] → a[i+1,j] 的有序对;
- 查找表 s 的行列索引直接对应 b 中的 (x,y),因此 s[x,y] 可 O(1) 返回映射值;
- 最终索引 s[pairs[...,0], pairs[...,1]] 是纯 NumPy 广播操作,无显式循环,速度极快。
若原始值为 1-based(如本例),但希望内存更紧凑,可统一转为 0-based 索引:
s = np.full((max_val, max_val), -1) s[b[:, 0]-1, b[:, 1]-1] = b[:, 2] pairs_0b = swv(a, 2, axis=0) - 1 c = s[pairs_0b[..., 0], pairs_0b[..., 1]]
⚠️ 稀疏索引场景:Pandas 合并方案(稳健替代)
当 b 中的数值跨度极大但实际出现稀疏(如取值来自 1~50000,但仅 1000 个有效对),构建稠密 s 会浪费大量内存且显著拖慢(实测可达秒级)。此时应改用基于哈希的关联查询:
import pandas as pd
# 将滑动窗口结果展平为 DataFrame,列名为 'x','y'
df_pairs = pd.DataFrame(
swv(a, 2, axis=0).reshape(-1, 2),
columns=['x', 'y']
)
# 将规则表 b 转为 DataFrame
df_rules = pd.DataFrame(b, columns=['x', 'y', 'val'])
# 左连接:按 (x,y) 匹配,取 val 列
merged = df_pairs.merge(df_rules, on=['x', 'y'], how='left')
# 恢复为二维数组:(n-1) × cols
c = merged['val'].to_numpy().reshape(-1, a.shape[1])
该方案时间复杂度为 O(N log N)(哈希合并),但内存占用恒定,对稀疏规则鲁棒性强。基准测试显示:在 1000×1000 密集场景下,NumPy 方案比 Pandas 快约 10 倍;而在 0.04% 稀疏度下,Pandas 反而快 100 倍以上。
✅ 总结与选型建议
| 场景 | 推荐方案 | 时间复杂度 | 内存特征 | 适用条件 |
|---|---|---|---|---|
| 值域紧凑(如 1~100,全覆盖或高密度) | NumPy 查表 + sliding_window_view | O(n×m) | O(K²)(K 为最大值) | K |
| 值域稀疏/超大(如 1~10⁶,仅数百有效对) | Pandas merge | O(n×m log(n×m)) | O(n×m + |b|) | 任意分布,内存友好 |
无论选用哪种方式,均应避免手写 for 循环——它在 a 较大时性能断崖式下降。始终优先利用 NumPy 的向量化能力或 Pandas 的优化哈希引擎,确保算法可扩展至百万级数据规模。










