第三章 数据清洗实战:异常值检测与缺失值处理
各位同行,欢迎来到数据清洗这一章。说实话,我在风电场干了这么多年,最头疼的往往不是算法选型,而是数据本身的质量问题。你想想看,一台风机每天产生几万条数据,里面混着传感器漂移、通信中断、极端工况……如果不把这些「脏数据」处理干净,后面再漂亮的模型也是白搭。
今天咱们就聊聊数据清洗的三个核心动作:异常值检测、缺失值处理、数据标准化。我会结合自己踩过的坑,把实战经验掰开揉碎了讲。
3.1 异常值检测:别让坏数据带偏了方向
异常值检测,说白了就是找出那些「不合理」的数据点。比如风速突然跳到50m/s,或者功率曲线完全偏离理论范围。我个人习惯先做可视化,看一眼数据分布,心里有个底。
3.1.1 阈值法:简单粗暴但有效
阈值法是最直接的方法。根据物理常识或行业标准,设定上下限。比如风速一般不会超过25m/s,功率不会超过额定值的1.2倍。
# 阈值法示例(Python)
import pandas as pd
import numpy as np
# 假设df是风电场数据,包含'风速'和'功率'两列
df = pd.read_csv('wind_farm_data.csv')
# 设定物理阈值
风速上限 = 25 # m/s
功率上限 = 1.2 * 1500 # 额定功率1500kW,留20%余量
# 标记异常
异常风速 = df['风速'] > 风速上限
异常功率 = df['功率'] > 功率上限
# 处理:删除或替换
df_clean = df[~(异常风速 | 异常功率)]
print(f"删除了 {len(df) - len(df_clean)} 条异常数据")
3.1.2 四分位法:更智能的统计方法
阈值法依赖人工经验,而四分位法(IQR)完全基于数据本身的分布。它把数据分成四等份,取Q1(25%分位)和Q3(75%分位),然后定义异常值为超出 [Q1 - 1.5*IQR, Q3 + 1.5*IQR] 范围的数据。
# 四分位法示例
def detect_outliers_iqr(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = (data[column] < lower_bound) | (data[column] > upper_bound)
return outliers
# 对功率列检测异常
功率异常 = detect_outliers_iqr(df, '功率')
df_clean = df[~功率异常]
3.2 缺失值处理:补还是不补?这是个问题
数据缺失在风电场太常见了。传感器故障、通信中断、维护停机……都会导致数据断档。处理缺失值,我一般分三步走:先看缺失比例,再看缺失模式,最后选方法。
3.2.1 删除法:简单但代价高
如果某列缺失超过70%,或者某行缺失的关键字段太多,直接删除是最省事的。但要注意,删除会损失信息,尤其是时间序列数据,删掉一行可能破坏连续性。
# 删除缺失比例过高的列
threshold = 0.7 # 70%缺失就删除
df_clean = df.dropna(thresh=len(df) * (1 - threshold), axis=1)
# 删除缺失关键字段的行
df_clean = df_clean.dropna(subset=['风速', '功率'])
3.2.2 插值法:更精细的补全策略
插值法利用已有数据推断缺失值。常用的有线性插值、多项式插值、时间序列插值等。对于风电场数据,我个人偏爱线性插值,因为它简单且物理意义明确——风速和功率在短时间内变化是连续的。
# 线性插值示例
df_interpolated = df.interpolate(method='linear', limit_direction='both')
# 时间序列插值(考虑时间间隔)
df['时间'] = pd.to_datetime(df['时间'])
df = df.set_index('时间')
df_interpolated = df.interpolate(method='time')
3.3 数据标准化:让不同量纲的数据「对齐」
风电场数据里,风速单位是m/s,功率是kW,温度是℃……量纲不同,直接扔进模型会导致某些特征被放大。标准化的目的,就是把所有特征拉到同一个尺度上。
3.3.1 Z-score标准化:最常用的方法
Z-score把数据转换成均值为0、标准差为1的分布。公式很简单:z = (x - μ) / σ。它适合数据近似正态分布的情况。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df_scaled = pd.DataFrame(
scaler.fit_transform(df[['风速', '功率', '温度']]),
columns=['风速', '功率', '温度']
)
3.3.2 Min-Max标准化:把数据压缩到[0,1]区间
Min-Max标准化适合数据有明确边界的情况,比如风速0~25m/s。公式:x' = (x - min) / (max - min)。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
df_scaled = pd.DataFrame(
scaler.fit_transform(df[['风速', '功率']]),
columns=['风速', '功率']
)
3.4 知识体系总览
下面这张图总结了本章的核心逻辑,我画成了流程图,方便你对照着看。
3.5 实战小结
数据清洗没有银弹,关键是根据数据特点灵活选择。我个人的工作流是:
- 先看数据概览:用describe()和info()快速了解缺失和分布
- 可视化检查:画箱线图看异常,画折线图看缺失模式
- 异常值处理:物理阈值优先,统计方法辅助
- 缺失值处理:小缺口插值,大缺口删除或标记
- 标准化:根据模型需求选择Z-score或Min-Max
公众号:蓝海资料掘金营,微信deep3321