
本文介绍如何不使用 Python 循环,仅用 NumPy 原生向量化操作,将分段边界列表(如 [0, 2, 5, 7])快速展开为对应索引数组(如 [0,0,1,1,1,2,2]),重点对比 np.repeat + np.diff 与 cumsum 两种高性能实现。
本文介绍如何不使用 python 循环,仅用 numpy 原生向量化操作,将分段边界列表(如 `[0, 2, 5, 7]`)快速展开为对应索引数组(如 `[0,0,1,1,1,2,2]`),重点对比 `np.repeat + np.diff` 与 `cumsum` 两种高性能实现。
在科学计算与数据预处理中,常需将一个表示区间划分的“分割点列表”(split list)转换为长度为总长度 N 的索引映射数组:每个位置 j 所属的区间编号即为其值。例如,给定 T = [0, 2, 5, 7](隐含 3 个区间:[0,2), [2,5), [5,7)),目标是生成 D = [0, 0, 1, 1, 1, 2, 2]。
最直观的实现依赖显式循环或列表推导式(如 np.concatenate([np.full(...)])),但存在明显性能瓶颈和内存开销。以下是两种真正高效的纯向量化方案:
✅ 方案一:np.diff + np.arange + np.repeat(推荐)
该方法语义清晰、执行最快,充分利用 NumPy 的广播与重复机制:
import numpy as np T = np.array([0, 2, 5, 7]) n = np.diff(T) # → [2, 3, 2]:各区间长度 out = np.repeat(np.arange(len(n)), n) # → [0,0,1,1,1,2,2] print(out) # [0 0 1 1 1 2 2]
✅ 优势:单次内存分配、零 Python 循环、可读性强;
✅ 进阶写法(Python ≥3.8,一行解决):
out = np.repeat(np.arange(len(n := np.diff(T))), n)
✅ 方案二:np.zeros + 索引赋值 + np.cumsum
利用前缀和思想,在区间起始点注入增量:
T = np.array([0, 2, 5, 7]) out = np.zeros(T[-1], dtype=int) # 初始化全 0 数组,长度 = T[-1] = 7 out[T[1:-1]] = 1 # 在内部分割点(2,5)处设为 1 → [0,0,1,0,0,1,0] out = np.cumsum(out) # 前缀和 → [0,0,1,1,1,2,2]
⚠️ 注意:此方法要求 T[0] == 0(题目已保证),且 T 严格递增;若 T 含重复值或非单调,结果未定义。
⚠️ 对比与选型建议
| 方法 | 时间复杂度 | 内存占用 | 可读性 | 适用场景 |
|---|---|---|---|---|
repeat + diff |
O(N) | O(N) | ★★★★☆ | 默认首选,通用性强 |
cumsum |
O(N) | O(N) | ★★★☆☆ | 当需复用 out 数组或后续需累加逻辑时更自然 |
重要提醒:两种方法均假设输入
T是严格递增的整数数组,首项为0,末项为总长度N。若原始数据不满足,请先校验:assert T[0] == 0 and np.all(np.diff(T) >= 0)。
综上,np.repeat(np.arange(len(np.diff(T))), np.diff(T)) 是目前最简洁、高效且符合 NumPy 编程范式的解决方案,应作为标准工具函数封装复用。










