
本文介绍三种无需显式循环的 numpy 方法,将形如 [0, a, b, ..., n] 的分割点列表快速转换为长度为 n 的索引数组,其中每个区间 [t[i], t[i+1]) 对应重复的索引 i。
本文介绍三种无需显式循环的 numpy 方法,将形如 [0, a, b, ..., n] 的分割点列表快速转换为长度为 n 的索引数组,其中每个区间 [t[i], t[i+1]) 对应重复的索引 i。
在科学计算与数据预处理中,常需将一组单调递增的分割点(如段起始偏移)映射为逐元素的段标识索引数组。例如,给定分割列表 T = [0, 2, 5, 7](表示三段:[0,2), [2,5), [5,7)),目标是生成长度为 7 的数组 D = [0, 0, 1, 1, 1, 2, 2],使 D[j] = i 当且仅当 j ∈ [T[i], T[i+1])。
最直观的实现是使用 Python 循环拼接,但效率低下。以下是三种向量化、高性能的替代方案:
✅ 方法一:np.diff + np.repeat(推荐)
利用 np.diff(T) 得到各段长度,再用 np.repeat 按长度重复对应段索引:
import numpy as np T = [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]
简洁、可读性强,时间复杂度 O(N),内存开销最优。Python ≥3.8 还可写成单行:
out = np.repeat(np.arange(len(n := np.diff(T))), n)
✅ 方法二:np.zeros + 索引赋值 + np.cumsum
构造全零数组,在每个段起点(除首尾)置 1,再累加:
out = np.zeros(T[-1], dtype=int) # 长度为 N = T[-1] out[T[1:-1]] = 1 # 在 [2,5] 处设 1 → out[2]=1, out[5]=1 out = np.cumsum(out) # → [0,0,1,1,1,2,2]
该方法逻辑清晰,适合理解“分段累加”本质,但需注意 T[1:-1] 不包含 0 和 T[-1](后者已超界),且对重复分割点鲁棒性略低。
⚠️ 注意事项
- 所有方法均要求
T严格递增、首项为0、末项为总长度N; - 若
T含重复值(如[0,2,2,5]),np.diff会产生 0,np.repeat将跳过该段(安全);而cumsum法在T[1:-1]中重复索引会覆盖,需预处理去重; -
dtype=int显式指定可避免默认浮点类型,节省内存并提升后续整数运算效率。
综上,np.repeat(np.arange(len(np.diff(T))), np.diff(T)) 是兼顾性能、简洁性与可维护性的首选方案,完全避免 Python 循环,在百万级数据上提速可达 10–100 倍。










