4. 索引与切片在工程数据筛选中的应用
在多通道传感器采集系统中,一次实验往往产生形如 (采样点数, 通道数) 的二维数组。例如 8 个通道、采样率 1000 Hz、采集 10 秒,就得到 (10000, 8) 的数据矩阵。工程分析真正需要的,通常只是其中某个时间段、某几个通道,或满足某种幅值条件的样本。NumPy 的索引与切片正是完成这类"数据裁剪"的基础工具,本节围绕基本索引、切片、布尔索引和花式索引四种手段,说明它们各自适合解决什么问题、如何组合使用。
基本索引:定位单个通道与单个采样点
NumPy 的二维数组索引写作 a[行, 列],行对应采样点(时间),列对应通道。约定本文中第 0 列为 1 号通道,依次类推。取第 3 个采样点、第 2 通道的值写为 data[2, 1];只写一个下标 data[2] 则返回第 3 个采样点的全部 8 个通道值,是一个长度为 8 的一维数组。若想取出整列(即某通道的完整时间序列),使用 data[:, 1],冒号表示"该维全部取"。这一步在工程上非常常用:把某一通道单独抽出来,才能做后续的频谱分析或统计。
需要特别注意,基本索引返回的是视图而非副本。对 ch = data[:, 1] 做原地修改会直接影响原始矩阵。若希望独立处理,应显式调用 data[:, 1].copy()。这一区别在数据清洗阶段容易引发"改了副本、原数据也变了"的隐蔽错误。
切片:按时间段截取连续样本
切片语法为 start:stop:step,左闭右开。采样率 1000 Hz 时,第 2 秒到第 5 秒对应下标 2000 到 5000,因此 data[2000:5000, :] 得到这 3 秒内全部通道的数据,形状为 (3000, 8)。若只关心第 1、3、5 通道在这段时间的数据,可写 data[2000:5000, [0, 2, 4]],这里列方向用了花式索引,行方向仍是切片,二者可以自然组合。
步长参数在降采样场景中很有价值。原始 1000 Hz 数据若每 10 点取 1 点,data[::10, :] 即得到等效 100 Hz 的序列,形状变为 (1000, 8)。负步长 data[::-1] 可反转时间顺序,用于检查信号尾部或做对称填充前的预处理。切片同样是视图,且切片范围超出数组边界时不会报错,而是自动截断到有效范围,这一点与基本索引越界会抛 IndexError 不同,编写健壮的筛选代码时应留意。
布尔索引:按条件筛选样本
布尔索引是工程数据筛选中威力最大的手段。先构造与目标维度等长的布尔数组,再用它作为下标。例如要找出 1 号通道幅值超过 2.5 V 的所有采样点:
import numpy as np
# 假设 data 形状为 (10000, 8),单位 V
ch1 = data[:, 0]
mask = ch1 > 2.5
hits = data[mask] # 形状 (N, 8),N 为满足条件的点数
times = np.arange(10000) / 1000.0
hit_times = times[mask] # 对应的时间戳
布尔索引返回的是副本,形状由 True 的个数决定,因此结果长度不固定,适合"找出所有异常样本"这类需求。多个条件用 &、|、~ 组合,每个条件必须加括号,例如 (ch1 > 2.5) & (ch1 < 3.8) 表示幅值落在 2.5 至 3.8 V 之间的样本。若想同时约束多个通道,可写 data[(data[:, 0] > 2.5) & (data[:, 3] < 0.1)],得到 1 号通道偏高且 4 号通道接近零的采样点,这类组合条件常用于定位传感器异常或工况切换时刻。
工程上还常用 np.where 获取满足条件的下标元组,便于进一步定位:idx = np.where(ch1 > 2.5)[0] 返回一维下标数组,可直接用于计算首次超限时刻 idx[0] / 1000.0。若只需判断"是否存在超限",用 np.any(ch1 > 2.5) 更直接,避免生成大数组。
花式索引:按指定顺序抽取通道或样本
花式索引用一个整数数组作为下标,返回副本,且结果顺序由下标数组决定。取通道顺序为 5、2、7 的数据写为 data[:, [4, 1, 6]],结果形状 (10000, 3),列顺序即 5 号、2 号、7 号通道。这在需要按物理布局重排通道、或只保留部分通道做建模时非常方便。花式索引也支持重复下标,例如 data[:, [0, 0, 1]] 会把 1 号通道复制两列,可用于构造对比特征。
行方向的花式索引适合抽取离散采样点,例如 data[[100, 2500, 7000], :] 取出三个指定时刻的全部通道值。若同时给行、列都传整数数组,NumPy 会按"配对"方式取值:data[[0, 1], [2, 3]] 返回 [data[0,2], data[1,3]],而不是 2×2 子矩阵。要得到子矩阵需借助 np.ix_:data[np.ix_([0, 1], [2, 3])]。这一配对规则是初学者最容易出错的地方,务必区分。
组合使用与工程实践要点
真实的数据筛选很少只用一种索引。一个典型流程是:先用切片锁定稳态时间段,再用布尔索引剔除该段内的异常点,最后用花式索引按需要的通道顺序输出。例如:
seg = data[2000:5000, :] # 2~5 s 稳态段
mask = np.abs(seg[:, 0]) < 3.0 # 1 号通道未超限
clean = seg[mask][:, [0, 2, 4]] # 剔除异常后取 1、3、5 通道
print(clean.shape) # 假设剩余 2870 点 → (2870, 3)
这里 seg[mask] 先按行筛选,再对结果做列花式索引,两步都产生副本,因此 clean 与原始 data 完全独立。若数据量很大,应尽量避免在循环中反复做布尔索引,可先计算一次掩码再复用;对超大数组,np.where 配合整数下标有时比布尔掩码更省内存。
另一个实践细节是时间轴对齐。切片得到的是连续下标区间,布尔索引得到的是离散下标集合,二者混用时务必用同一套下标基准换算时间,否则容易把"第 2000 个点"误当成"第 2 秒"。建议在数据加载后立即建立 t = np.arange(n) / fs 的时间向量,所有筛选结果都通过同一 t 取时间戳,保证全文与图示中的时间参照一致。
掌握这四种索引后,面对多通道传感器数据,无论是截取工况段、剔除饱和点、重排通道,还是抽取特征样本,都能用一两行 NumPy 表达式完成,而不必写显式循环。这正是向量化筛选在工程数据处理中的核心价值。