3、数据预处理:缺失值处理、异常值清洗、数据标准化与归一化
数据预处理,说白了就是给模型「喂饭」之前,先把食材洗干净、切好块。我见过太多同学拿到遥测数据就直接往模型里塞,结果跑出来的曲线跟心电图似的——全是坑。嗯,咱们今天就把这块硬骨头啃下来。
核心观点:遥测数据天生带「病」——传感器漂移、网络丢包、时钟不同步,这三座大山不搬掉,模型再牛也白搭。
3.1 缺失值处理:别让「空」变成「坑」
遥测数据里缺值太常见了。我记得有一次做卫星电源遥测分析,一个关键温度传感器在轨道阴影期连续丢了 12 个采样点。如果不处理,模型会把「没数据」当成「温度为零」,直接触发误报警。
3.1.1 缺失类型识别
先搞清楚缺值是哪种「缺」:
| 缺失类型 | 特征 | 常见场景 |
|---|---|---|
| 完全随机缺失 | 缺值与任何变量无关 | 网络瞬时抖动丢包 |
| 随机缺失 | 缺值与其他已知变量相关 | 高负载时 CPU 温度传感器采样失败 |
| 非随机缺失 | 缺值本身与缺失原因相关 | 传感器损坏后持续无输出 |
我曾经踩过的坑:有一次把非随机缺失当随机缺失处理,用均值填充了连续 200 个缺值。结果模型学到的「正常模式」全是填充出来的假数据,上线第一天就漏报了一次真实故障。后来我养成了习惯——先做缺失模式分析,再选填充策略。
3.1.2 填充策略选择
我个人习惯按缺失率分档处理:
- 缺失率 < 5%:直接删除该采样点,或者用前后均值填充。简单粗暴,效果不差。
- 缺失率 5%~20%:用线性插值或时间加权填充。遥测数据通常有短期相关性,前 3 个点的加权平均往往比全局均值靠谱。
- 缺失率 > 20%:考虑用 KNN 或矩阵分解。但说实话,超过 30% 的缺失,我建议你回去检查采集链路——数据质量本身就有问题。
# 我个人常用的插值方案
import pandas as pd
import numpy as np
def fill_telemetry_missing(df, column, method='time_weighted'):
if method == 'time_weighted':
# 用前 3 个有效点的指数加权平均
weights = np.array([0.5, 0.3, 0.2])
df[column] = df[column].fillna(
df[column].rolling(4, min_periods=1)
.apply(lambda x: np.dot(x.dropna().tail(3), weights[:len(x.dropna().tail(3))]))
)
return df
3.2 异常值清洗:把「脏数据」挡在门外
遥测数据里的异常值,说白了就两种:一种是传感器抽风(比如突然跳变到满量程),另一种是真实异常(比如设备真的过温了)。我们的任务是只清洗前者,保留后者——这活儿比想象中难。
3.2.1 异常检测三板斧
我一般按这个顺序来:
- 物理边界过滤:先查数据手册,把超出传感器量程的值直接干掉。比如电压遥测范围是 0~5V,出现 12V 肯定是坏数据。
- 统计方法筛查:用 IQR(四分位距)或 Z-score 找出统计离群点。遥测数据通常不是正态分布,我更喜欢用 IQR——它不受极端值影响。
- 时序上下文校验:单个点异常不可怕,连续 3 个点同时异常才值得警惕。我会检查每个异常点前后 5 个采样点的变化率。
一个小技巧:对于周期性遥测数据(比如每天同一时刻的 CPU 使用率),我会先做「同期对比」——把当前值和过去 7 天同一时刻的值做比较。如果偏离超过 3 倍标准差,再标记为候选异常。这样能有效避免把正常的业务高峰误判为异常。
3.2.2 清洗 vs 保留的决策树
遇到异常值,我通常会问自己三个问题:
- 这个值物理上可能吗?(比如温度 -273°C 直接删)
- 这个值在时间序列上是否孤立?(孤立点大概率是传感器噪声)
- 这个值是否与上下游指标矛盾?(比如 CPU 温度飙升但负载没变,可能是传感器故障)
如果三个答案都是「是」,那就清洗掉。否则,我会打上标签保留——这些「可疑异常」往往是模型训练的好素材。
3.3 数据标准化与归一化:让所有特征「平起平坐」
为什么需要这一步?你想想看,电压遥测是 0~5V,温度是 20~80°C,内存使用率是 0~100%。如果不做缩放,模型会天然认为「数值大的特征更重要」——这显然是错的。
3.3.1 标准化 vs 归一化
| 方法 | 公式 | 输出范围 | 适用场景 |
|---|---|---|---|
| Z-score 标准化 | (x - μ) / σ | 无固定范围,均值为 0 | 数据近似正态分布,或使用 SVM、PCA 等对尺度敏感的算法 |
| Min-Max 归一化 | (x - min) / (max - min) | [0, 1] | 数据有明确边界,或使用神经网络、KNN 等算法 |
| Robust 缩放 | (x - median) / IQR | 无固定范围 | 数据包含较多异常值,不想让异常值影响缩放参数 |
我的经验:做遥测基线模型,我首选 Robust 缩放。因为遥测数据里总有那么几个「野点」,如果用 Min-Max,这些野点会把正常数据压缩到一个小角落里。Robust 缩放用中位数和四分位距,对异常值不敏感——说白了就是更扛造。
3.3.2 实操中的两个坑
坑一:先切分再缩放。我见过有人先对整个数据集做归一化,再切训练集和测试集。这会导致测试集信息泄露——因为归一化的 min/max 已经包含了测试集的数据。正确做法是先切分,再用训练集的参数去缩放测试集。
# 正确的做法
from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
X_train, X_test = train_test_split(data, test_size=0.2, random_state=42)
scaler = RobustScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # 只用训练集的参数
坑二:不同传感器用不同缩放器。遥测数据里,电压和温度的量纲完全不同。我习惯为每个传感器单独训练一个缩放器,而不是对所有特征统一缩放。这样能保留每个传感器的独立分布特征。
3.4 本章知识体系
下面这张图把数据预处理的三个核心步骤串起来了。你可以把它当作操作手册——拿到遥测数据后,按这个流程走一遍,基本不会出大问题。
最后说一句:数据预处理没有银弹。我做了这么多年,最大的体会就是——每次拿到新数据,先花 30 分钟做可视化探索。把缺失分布、异常模式、特征范围都画出来,心里有数了再动手。磨刀不误砍柴工,这话在数据预处理上尤其适用。