3、数据清洗实战:缺失值处理(插值/删除)、异常值检测(3-Sigma/箱线图)、重复数据处理

各位同学,欢迎来到数据清洗的实战环节。

说实话,在风机RUL预测这个领域,我踩过最大的坑,不是模型选得不对,也不是参数调得不好,而是——数据太脏了。你想想看,风机在野外风吹日晒,传感器时不时抽风,通讯链路偶尔断连,采集到的数据要是直接扔进模型,那结果基本就是「垃圾进,垃圾出」。

我个人习惯,拿到数据的第一件事,不是急着画图,也不是跑模型,而是先做数据清洗。这一步做好了,后面能省80%的调试时间。今天我们就来聊聊数据清洗的三个核心动作:缺失值处理、异常值检测、重复数据处理。

3.1 缺失值处理:插值还是删除?

缺失值,说白了就是数据采集过程中「断片了」。风机SCADA系统里,这种情况太常见了。比如某个温度传感器突然掉线,或者网络延迟导致数据包丢失,都会产生NaN。

处理缺失值,我一般分三步走:

  1. 先看看缺失比例——如果某个特征缺失超过50%,我建议直接删掉这个特征。留着也是噪声。
  2. 再看缺失模式——是随机缺失,还是连续缺失?连续缺失往往意味着传感器故障,要特别小心。
  3. 最后选方法——缺失少用插值,缺失多用删除。

我的经验法则:

  • 缺失率 < 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 知识体系总览

说了这么多,我们来画个图,把数据清洗的整个流程串起来。这样你心里就有个谱了。

数据清洗实战流程 原始SCADA数据 缺失值处理 插值(线性/时间) 删除(缺失率高) 异常值检测 3-Sigma法则 箱线图(IQR) 重复数据处理 完全重复 / 近似重复

这张图把今天的内容串起来了。从原始数据出发,先处理缺失值,再检测异常值,最后清理重复数据。每一步都有对应的策略和代码实现。

嗯,数据清洗这部分,说白了就是「磨刀不误砍柴工」。你花1小时把数据洗干净,后面建模可能省10小时。我见过太多人急着调模型,结果被脏数据坑得死去活来。所以,别急,先把数据搞干净。

本章小结:

  • 缺失值处理:插值优先,删除为辅,结合缺失率判断
  • 异常值检测:3-Sigma适合正态数据,箱线图更鲁棒
  • 重复数据处理:先查原因,再动手删,注意近似重复
  • 所有操作都要结合业务逻辑,别盲目删除

专注资料整理