3、数据清洗实战:缺失值处理(插值/删除)、异常值检测(3-Sigma/箱线图)、重复数据处理
各位同学,欢迎来到数据清洗的实战环节。
说实话,在风机RUL预测这个领域,我踩过最大的坑,不是模型选得不对,也不是参数调得不好,而是——数据太脏了。你想想看,风机在野外风吹日晒,传感器时不时抽风,通讯链路偶尔断连,采集到的数据要是直接扔进模型,那结果基本就是「垃圾进,垃圾出」。
我个人习惯,拿到数据的第一件事,不是急着画图,也不是跑模型,而是先做数据清洗。这一步做好了,后面能省80%的调试时间。今天我们就来聊聊数据清洗的三个核心动作:缺失值处理、异常值检测、重复数据处理。
3.1 缺失值处理:插值还是删除?
缺失值,说白了就是数据采集过程中「断片了」。风机SCADA系统里,这种情况太常见了。比如某个温度传感器突然掉线,或者网络延迟导致数据包丢失,都会产生NaN。
处理缺失值,我一般分三步走:
- 先看看缺失比例——如果某个特征缺失超过50%,我建议直接删掉这个特征。留着也是噪声。
- 再看缺失模式——是随机缺失,还是连续缺失?连续缺失往往意味着传感器故障,要特别小心。
- 最后选方法——缺失少用插值,缺失多用删除。
我的经验法则:
- 缺失率 < 5%:用插值(线性插值或时间插值)
- 缺失率 5% ~ 20%:用前向填充或后向填充
- 缺失率 > 20%:直接删除该特征或该样本
代码实现其实很简单。我常用的方式是pandas自带的插值方法:
import pandas as pd
import numpy as np
# 模拟一段风机振动数据,中间有缺失
data = pd.Series([0.5, 0.6, np.nan, 0.7, 0.8, np.nan, np.nan, 0.9])
# 线性插值
data_interpolated = data.interpolate(method='linear')
print(data_interpolated)
# 时间序列插值(如果索引是时间)
# data_interpolated = data.interpolate(method='time')
小技巧:对于风机数据,我建议优先使用时间插值(method='time')。因为风机的物理量变化是连续的,时间插值更符合实际情况。线性插值在时间间隔不均匀时会有偏差。
3.2 异常值检测:3-Sigma与箱线图
异常值,就是那些「离谱」的数据点。比如风速突然飙到100m/s,或者发电机温度瞬间跳到200°C——这明显是传感器故障,不是真实物理现象。
检测异常值,我常用的有两种方法:3-Sigma法则和箱线图法。
3-Sigma法则
这个方法基于正态分布假设。简单说,就是数据落在均值±3个标准差之外的概率极低(不到0.3%),所以这些点可以视为异常。
我在项目中遇到过一个问题:风机数据往往不是严格正态的,尤其是风速和功率数据,偏态很明显。这时候直接用3-Sigma会误删很多正常数据。怎么办?我的做法是先做对数变换或Box-Cox变换,让数据接近正态,再用3-Sigma。
import numpy as np
def detect_outliers_3sigma(data):
mean = np.mean(data)
std = np.std(data)
lower_bound = mean - 3 * std
upper_bound = mean + 3 * std
outliers = (data < lower_bound) | (data > upper_bound)
return outliers
# 示例
vibration = np.array([0.5, 0.6, 0.55, 0.65, 2.3, 0.58, 0.62])
outliers = detect_outliers_3sigma(vibration)
print(f"异常值索引: {np.where(outliers)[0]}")
箱线图法
箱线图不依赖正态分布假设,它用四分位数来定义异常。具体来说,IQR(四分位距)= Q3 - Q1,异常值定义为小于Q1 - 1.5*IQR或大于Q3 + 1.5*IQR的点。
我个人更偏爱箱线图法,原因很简单:它更鲁棒。风机数据里经常有极端值,3-Sigma会被这些极端值「带偏」,而箱线图不受极端值影响。
def detect_outliers_iqr(data):
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = (data < lower_bound) | (data > upper_bound)
return outliers
# 示例
outliers_iqr = detect_outliers_iqr(vibration)
print(f"IQR法异常值索引: {np.where(outliers_iqr)[0]}")
注意:我曾经犯过一个错误——把所有异常值都删掉。后来发现,有些「异常值」其实是风机故障的前兆信号。比如振动值突然升高,可能预示着轴承开始磨损。所以,检测出异常值后,一定要结合业务逻辑判断:是传感器噪声,还是真实物理异常?
3.3 重复数据处理
重复数据,听起来简单,但坑不少。风机SCADA系统里,重复数据通常来自两种情况:
- 完全重复:同一时间戳下,多条完全一样的数据。这往往是数据采集程序bug导致的。
- 近似重复:时间戳略有偏差,但数值几乎一样。比如同一时刻采集了两次,时间差了几毫秒。
处理重复数据,我的原则是:先查原因,再动手删。
# 检测完全重复
duplicates = df.duplicated()
print(f"完全重复行数: {duplicates.sum()}")
# 删除完全重复
df_clean = df.drop_duplicates()
# 检测近似重复(基于时间戳容差)
df['time_diff'] = df['timestamp'].diff().dt.total_seconds()
near_duplicates = df[df['time_diff'] < 0.1] # 时间差小于0.1秒
print(f"近似重复行数: {len(near_duplicates)}")
避坑指南:我曾经在处理一个风场数据时,发现重复率高达30%。一开始以为是采集问题,后来一查,原来是数据合并时把训练集和测试集重复拼接了。所以,遇到重复数据,先检查数据来源和合并逻辑,别急着删。
3.4 知识体系总览
说了这么多,我们来画个图,把数据清洗的整个流程串起来。这样你心里就有个谱了。
这张图把今天的内容串起来了。从原始数据出发,先处理缺失值,再检测异常值,最后清理重复数据。每一步都有对应的策略和代码实现。
嗯,数据清洗这部分,说白了就是「磨刀不误砍柴工」。你花1小时把数据洗干净,后面建模可能省10小时。我见过太多人急着调模型,结果被脏数据坑得死去活来。所以,别急,先把数据搞干净。
本章小结:
- 缺失值处理:插值优先,删除为辅,结合缺失率判断
- 异常值检测:3-Sigma适合正态数据,箱线图更鲁棒
- 重复数据处理:先查原因,再动手删,注意近似重复
- 所有操作都要结合业务逻辑,别盲目删除