第2章:数据预处理基础——缺失值处理与异常值检测
各位同学好,我是你们的老朋友。上一章我们聊了数据采集和基础概览,今天咱们进入一个更“脏活累活”的阶段——数据预处理。
说实话,我在电力行业摸爬滚打这么多年,见过太多“漂亮”的原始数据,一跑模型就崩。为什么?因为数据里藏着各种“坑”。缺失值、异常值,这两个东西不处理好,后面的分析全是白搭。今天我就把压箱底的经验掏出来,咱们一个一个说。
2.1 缺失值处理:别让“空”坑了你
电力数据里,缺失值太常见了。传感器故障、通信中断、人为误操作……原因五花八门。你想想看,一个变电站的负荷数据,某天突然跳了个空,你怎么办?
我个人习惯,先看缺失比例。如果缺失超过50%,这列数据基本可以放弃了。如果只是零星几个,那就要对症下药。
2.1.1 均值填充——简单粗暴,但有用
均值填充,说白了就是用这列数据的平均值去填那个空。比如某台变压器的日负荷数据,缺失了中午12点的值,我就用前后几天的平均值补上。
我在项目中遇到过一个小问题:如果数据本身波动很大,均值填充会“拉平”曲线,导致后续分析失真。所以,均值填充只适合数据相对平稳的场景。
import pandas as pd
import numpy as np
# 示例数据:某变压器日负荷(kW)
data = {'时间': ['2024-01-01 00:00', '2024-01-01 01:00', '2024-01-01 02:00', '2024-01-01 03:00'],
'负荷': [120, 115, None, 108]}
df = pd.DataFrame(data)
# 均值填充
mean_val = df['负荷'].mean()
df['负荷_填充'] = df['负荷'].fillna(mean_val)
print(df)
2.1.2 插值法——更“聪明”的填充
插值法,就是根据已知数据点,拟合出一条曲线,然后估算缺失点的值。常用的有线性插值、多项式插值、样条插值。
嗯,这里要注意:插值法假设数据是连续的、平滑的。电力负荷数据通常满足这个条件,所以插值法很实用。
# 线性插值
df['负荷_插值'] = df['负荷'].interpolate(method='linear')
print(df)
我曾经用插值法处理过一个风电场的风速缺失数据。风速变化剧烈,线性插值效果不好,我换成了三次样条插值,效果立竿见影。所以,方法没有绝对好坏,得看数据特点。
2.1.3 删除法——最“狠”的方式
删除法,就是直接把有缺失的行或列删掉。简单、粗暴、有效,但代价是丢失信息。
我一般只在两种情况下用删除法:一是缺失比例极低(比如<1%),二是缺失值集中在某几个样本上,且这些样本本身就不重要。
# 删除含有缺失值的行
df_clean = df.dropna()
print(df_clean)
2.2 异常值检测:揪出“捣乱分子”
异常值,就是那些明显偏离正常范围的数据点。比如某台变压器负荷突然飙到10000kW,而正常只有500kW——这要么是设备故障,要么是数据录入错误。
为什么异常值要处理?因为很多算法(比如线性回归、K-means聚类)对异常值非常敏感。一个异常点,就能把模型带偏。我见过一个案例,就因为一个异常值,整个预测模型的误差率从5%飙升到30%。
2.2.1 3σ原则——经典统计方法
3σ原则,也叫拉依达准则。它假设数据服从正态分布,那么99.7%的数据会落在均值±3个标准差范围内。超出这个范围,就认为是异常值。
说白了,就是“三倍标准差之外,都是可疑分子”。
# 3σ原则检测异常值
mean = df['负荷'].mean()
std = df['负荷'].std()
threshold = 3 * std
df['是否异常_3sigma'] = (np.abs(df['负荷'] - mean) > threshold)
print(df)
2.2.2 箱线图法——更“稳健”的选择
箱线图法不依赖数据分布,它基于四分位数。具体来说,它用Q1(下四分位数)和Q3(上四分位数)定义了一个“正常范围”:
- 下界 = Q1 - 1.5 * IQR
- 上界 = Q3 + 1.5 * IQR
其中IQR = Q3 - Q1。超出这个范围的数据点,就是异常值。
我个人更喜欢箱线图法,因为它对极端值不敏感,而且能处理非正态分布的数据。我在处理光伏发电数据时,经常用这个方法。
# 箱线图法检测异常值
Q1 = df['负荷'].quantile(0.25)
Q3 = df['负荷'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df['是否异常_boxplot'] = (df['负荷'] < lower_bound) | (df['负荷'] > upper_bound)
print(df)
2.3 实战对比:两种方法怎么选?
我整理了一个对比表,方便你快速决策:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 3σ原则 | 数据近似正态分布 | 计算简单,理论成熟 | 对非正态分布不友好 |
| 箱线图法 | 任意分布,尤其是偏态分布 | 稳健,不受极端值影响 | 对样本量有要求(一般>20) |
你想想看,如果数据是电力负荷这种“有规律波动”的,我通常先用箱线图法。如果是传感器校准数据这种“理论上应该正态分布”的,3σ原则更合适。
2.4 处理流程:我的“三板斧”
最后,我分享一下自己的处理流程,算是“避坑指南”:
- 先看数据概览:用
df.info()和df.describe()快速了解缺失和异常情况。 - 缺失值处理:先判断缺失比例,再选方法。比例小用删除法,比例大用插值法或均值填充。
- 异常值检测:先用箱线图法“扫一遍”,再用3σ原则“复核”。如果两种方法都标记为异常,那基本可以确定是异常值。
- 处理异常值:可以删除,也可以用中位数或前后值替换。我个人习惯用中位数替换,因为中位数对异常值不敏感。
好了,这一章的内容就到这里。数据预处理虽然枯燥,但它是整个分析流程的基石。你想想看,地基没打好,房子能稳吗?下一章我们聊特征工程,到时候见。