3. 数据清洗与预处理:缺失值处理、异常值检测、数据标准化与归一化

各位同行,咱们接着聊。上一章我们把原始数据捞上来了,但说实话,工业现场的数据,那叫一个「脏」。传感器偶尔抽风、通讯丢包、人为误操作……这些数据要是直接扔进模型,结果绝对让你哭。我见过太多人,数据没洗干净就开始分析,最后得出一个「设备寿命还有三年」的结论,结果下个月就坏了。

所以,数据清洗与预处理,不是可有可无的步骤。它是整个预防性维护的基石。今天我就把这块硬骨头,掰开了揉碎了讲给你听。

数据清洗与预处理 缺失值处理 删除 / 填充 / 插值 异常值检测 3σ / IQR / 箱线图 标准化与归一化 Z-score / Min-Max 目标:干净、一致、可比较的数据 → 驱动准确的预防性维护决策

3.1 缺失值处理:别让「空」坑了你

工业数据里,缺失值太常见了。传感器断电、网络抖动、存储故障……都会导致数据点丢失。我个人习惯,拿到数据第一件事就是检查缺失率。

核心原则:缺失率 < 5% 可以填充;5%-20% 需要谨慎处理;> 20% 建议重新审视数据采集方案。

处理缺失值,主要有三种思路:

  • 直接删除:简单粗暴。如果缺失值占比很小(比如不到1%),而且随机分布,删了就删了。我在处理振动数据时经常这么干,因为缺失几个采样点对整体趋势影响微乎其微。
  • 填充:用均值、中位数、众数填充。但要注意——如果是时间序列数据,用前向填充(ffill)或后向填充(bfill)更合理。举个例子,温度传感器每10秒上报一次,中间丢了一两个点,用前一个值填充,比用整体均值靠谱得多。
  • 插值:线性插值、多项式插值、样条插值。我个人偏爱线性插值,简单且有效。尤其是设备缓慢退化过程中的数据,线性插值基本能还原真实趋势。
import pandas as pd
import numpy as np

# 读取数据
df = pd.read_csv('sensor_data.csv')

# 检查缺失值
print(df.isnull().sum())

# 前向填充(适合时间序列)
df['temperature'].fillna(method='ffill', inplace=True)

# 线性插值(适合缓慢变化的数据)
df['vibration'].interpolate(method='linear', inplace=True)

# 如果缺失太多,直接删除该行
df.dropna(subset=['pressure'], inplace=True)
我的经验:千万别对所有列用同一种填充方法。温度用前向填充,振动用插值,压力用均值——具体问题具体分析。我曾经在一个项目里,对所有缺失值统一用了均值填充,结果把设备早期故障的特征给「填平」了,差点误判。

3.2 异常值检测:揪出「捣乱分子」

异常值,说白了就是那些明显偏离正常范围的数据点。可能是传感器故障、通讯干扰,也可能是设备真的出问题了。所以,异常值不一定是坏事——它可能是故障的前兆。

我常用的方法有三种:

  1. 3σ 原则:假设数据服从正态分布,超出均值±3倍标准差的值视为异常。简单,但要求数据分布不能太偏。
  2. IQR 方法:四分位距法。用 Q1 - 1.5*IQR 和 Q3 + 1.5*IQR 作为上下界。这个方法对偏态分布更鲁棒。我个人更常用这个。
  3. 箱线图可视化:画个箱线图,异常点一目了然。我每次做数据探索,必先画箱线图。
# IQR 方法检测异常值
Q1 = df['vibration'].quantile(0.25)
Q3 = df['vibration'].quantile(0.75)
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 标记异常值
df['is_outlier'] = (df['vibration'] < lower_bound) | (df['vibration'] > upper_bound)

# 查看异常值
print(df[df['is_outlier']])
⚠️ 注意:异常值不一定要删除!我曾经处理一台离心压缩机的数据,发现振动值突然飙升后又回落。按常规方法,这应该标记为异常并剔除。但我留了个心眼,去现场一查——原来是轴承开始出现微裂纹,早期故障信号!所以,异常值要先判断是「坏数据」还是「真信号」。

3.3 数据标准化与归一化:让不同量纲的数据「对话」

你想想看,温度是几十度,振动是几个毫米/秒,压力是几百千帕。这些数据量纲不同,数值范围天差地别。如果直接扔进模型,数值大的特征会主导结果,数值小的特征就被淹没了。

所以,我们需要让它们站在同一起跑线上。常用的方法有两种:

方法 公式 适用场景 特点
Z-score 标准化 z = (x - μ) / σ 数据近似正态分布,有异常值 不改变数据分布形状,保留异常信息
Min-Max 归一化 x' = (x - min) / (max - min) 数据有明确边界,如0-100% 将数据压缩到[0,1]区间,受异常值影响大

我个人习惯:如果后续要用到距离计算的算法(比如KNN、SVM),优先用Z-score标准化。如果数据本身有物理上下界(比如电机负载率0-100%),用Min-Max归一化更直观。

from sklearn.preprocessing import StandardScaler, MinMaxScaler

# Z-score 标准化
scaler_std = StandardScaler()
df[['temperature', 'vibration', 'pressure']] = scaler_std.fit_transform(
    df[['temperature', 'vibration', 'pressure']]
)

# Min-Max 归一化
scaler_mm = MinMaxScaler()
df[['temperature', 'vibration', 'pressure']] = scaler_mm.fit_transform(
    df[['temperature', 'vibration', 'pressure']]
)
避坑指南:我曾经犯过一个错——先做了异常值剔除,再做了标准化。结果标准化后的数据,因为异常值被删了,均值和标准差都变了。后来我学乖了:先处理缺失值,再检测异常值(但不急着删),然后标准化,最后再决定异常值的去留。顺序很重要!

3.4 实战中的「组合拳」

说了这么多,实际项目中怎么用?我一般按这个流程走:

  1. 第一步:概览数据。用 df.info()、df.describe() 快速了解数据全貌。
  2. 第二步:处理缺失值。先看缺失率,再选方法。时间序列用前向填充或插值。
  3. 第三步:检测异常值。画箱线图,用IQR方法标记。但先别删,留着分析。
  4. 第四步:标准化/归一化。根据算法需求选择方法。
  5. 第五步:回头处理异常值。结合业务知识,判断是剔除还是保留。

嗯,这套流程我用了好几年,基本没出过大的偏差。当然,每个项目都有它的特殊性,灵活调整才是王道。

数据清洗这件事,说白了就是「磨刀不误砍柴工」。你花80%的时间把数据洗干净,后面20%的时间建模分析,结果往往比反过来要好得多。我见过太多人急着建模,结果被脏数据坑得团团转。记住:垃圾进,垃圾出。数据干净了,预防性维护才能真正落地。


公众号:蓝海资料掘金营,微信deep3321