第3章 数据预处理实战:时间序列数据清洗、异常值检测、缺失值填充方法
各位工程师朋友,欢迎来到数据预处理这一章。
说实话,在电力系统干了这么多年,我最大的体会就是:算法模型再牛,也架不住数据烂。你想想看,多能协同控制里,光伏、风电、负荷、储能……各路数据从不同传感器、不同采集系统汇过来,那叫一个“群魔乱舞”。
我个人习惯,拿到数据的第一件事,不是急着建模,而是先花60%的时间做数据预处理。别嫌慢,这一步省了,后面全是坑。
核心观点:数据预处理不是“清洁工”,而是“质检员”。它决定了后续所有分析的可靠性。
3.1 时间序列数据清洗——先把“毛刺”捋顺
时间序列数据,说白了就是带时间戳的数值流。比如光伏电站每5分钟上报的功率值,或者变电站每15分钟记录的电压数据。
清洗的第一步,是检查时间轴是否连续。我在项目中遇到过,某个风电场的数据因为GPS授时模块故障,时间戳跳了整整两个小时。如果不处理,后续的时序分析全乱套。
3.1.1 时间轴对齐与重采样
数据采集频率不一致,是家常便饭。有的传感器1分钟上报一次,有的10分钟才报一次。这时候就需要重采样。
import pandas as pd
# 假设原始数据索引是时间戳
df.index = pd.to_datetime(df.index)
# 重采样到15分钟间隔,取均值
df_resampled = df.resample('15T').mean()
# 如果某个时间段没有数据,用前向填充
df_filled = df_resampled.ffill()
嗯,这里要注意:resample 的 '15T' 表示15分钟。别写成 '15min',虽然也能用,但老版本可能不兼容。
3.1.2 重复值处理
重复数据很隐蔽。有时候是通信重发,有时候是数据库写入异常。
我曾经排查过一个微电网的功率波动问题,折腾了两天,最后发现是数据采集器重复发送了同一时刻的数据,导致平均功率计算错误。
# 检查重复行
duplicates = df[df.index.duplicated(keep='first')]
# 保留第一条,删除后续重复
df_clean = df[~df.index.duplicated(keep='first')]
小技巧:用 keep='first' 保留第一条记录。如果怀疑第一条也有问题,可以用 keep=False 全部删除,再插值填充。
3.2 异常值检测——揪出那些“离谱”的数据
异常值,就是那些明显偏离正常范围的数据点。比如光伏功率突然飙到5000kW,而实际装机容量才3000kW——这明显是传感器短路了。
检测方法有很多,我常用的就三种:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 3σ原则 | 数据近似正态分布 | 简单快速 | 对非正态分布效果差 |
| 箱线图法(IQR) | 任意分布 | 鲁棒性强 | 对尾部数据不敏感 |
| 移动平均偏差 | 时间序列趋势明显 | 考虑局部变化 | 参数选择依赖经验 |
3.2.1 3σ原则——最直观的方法
说白了,就是假设数据服从正态分布,超过均值±3倍标准差的数据,视为异常。
import numpy as np
mean = df['power'].mean()
std = df['power'].std()
threshold = 3
# 标记异常
df['is_anomaly'] = (np.abs(df['power'] - mean) > threshold * std)
但要注意,电力负荷数据往往有昼夜规律,直接对整个序列用3σ,可能会把夜间低谷期的正常低值误判为异常。我建议按时间段分组处理。
3.2.2 箱线图法——更鲁棒的选择
箱线图基于四分位数,不受极端值影响。IQR(四分位距) = Q3 - Q1。通常认为小于 Q1 - 1.5×IQR 或大于 Q3 + 1.5×IQR 的是异常。
Q1 = df['power'].quantile(0.25)
Q3 = df['power'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df['is_anomaly'] = (df['power'] < lower_bound) | (df['power'] > upper_bound)
避坑指南:我曾经在光伏数据上直接用箱线法,结果把阴天正常低功率全标成了异常。后来加了“功率变化率”条件——如果功率突变超过装机容量的20%,才判定为异常。效果好了很多。
3.3 缺失值填充——别让数据“断片”
缺失值在电力系统中太常见了。通信中断、传感器离线、存储故障……原因五花八门。
填充方法的选择,取决于缺失的模式和长度。
3.3.1 前向/后向填充——短缺失的首选
如果缺失不超过3个连续点,用前向填充(用上一个值补)基本够用。比如储能SOC数据,短时间内变化不大。
# 前向填充,最多填充2个连续缺失
df['soc'].fillna(method='ffill', limit=2, inplace=True)
3.3.2 线性插值——中等长度缺失
缺失5-10个点,用线性插值。它假设数据在缺失区间内线性变化,对缓慢变化的负荷数据很合适。
df['load'].interpolate(method='linear', inplace=True)
3.3.3 时间加权插值——考虑时间间隔
如果采样间隔不均匀,用 method='time',它会根据时间戳的实际间隔加权。
df['temperature'].interpolate(method='time', inplace=True)
3.3.4 模型预测填充——长缺失的终极方案
缺失超过20个连续点,简单插值就不靠谱了。我会用ARIMA或LSTM预测缺失段。
from statsmodels.tsa.arima.model import ARIMA
# 用缺失前的数据训练模型
train_data = df['wind_speed'].dropna().iloc[-100:]
model = ARIMA(train_data, order=(2,1,2))
model_fit = model.fit()
# 预测缺失的10个点
forecast = model_fit.forecast(steps=10)
个人经验:模型填充虽然精度高,但计算量大。我一般只在关键节点(比如并网点功率)用这个方法。普通传感器数据,用插值就够了。
3.4 知识体系总览
下面这张图,是我梳理的数据预处理核心逻辑。你可以把它当作操作手册。
这张图展示了从原始数据到干净数据的完整链路。每一步都有对应的技术手段,你可以根据实际情况灵活组合。
3.5 实战建议与避坑总结
最后,分享几条我用血泪换来的经验:
- 先看数据,再动手。 用
df.describe()和df.info()快速了解数据概况。别上来就写代码。 - 异常检测要结合业务。 光伏功率不可能为负,但负荷功率可以为负(比如用户反送电)。阈值设置要问现场工程师。
- 保留原始数据副本。 我习惯在预处理前做
df_raw = df.copy()。万一处理错了,还能回滚。 - 记录每一步操作。 用日志或注释写明:什么时间、用什么方法、处理了哪些数据。方便复盘。
一句话总结:数据预处理不是技术活,是良心活。你花多少心思,数据就给你多少回报。
公众号:蓝海资料掘金营,微信 deep3321