4. 数据清洗:缺失值处理、异常值检测与数据平滑技术

各位同学,咱们今天聊聊数据清洗。说实话,很多做预测的人容易忽略这一步,觉得模型调参才是重头戏。但我得说句实在话——数据不干净,模型再花哨也是白搭。我在项目里见过太多这种情况了:数据里藏着几个坏点,结果整个预测曲线都跑偏了。

数据清洗说白了就三件事:缺的补上、坏的去掉、毛刺磨平。咱们一个一个来。

4.1 缺失值处理

发电量数据里,缺失值太常见了。传感器掉线、通信中断、存储故障……原因五花八门。我个人习惯,拿到数据第一件事就是看看缺失率。

核心原则:缺失率低于5%,直接填充;5%-20%,需要谨慎选择方法;超过20%,建议重新评估数据质量。

常用的填充方法有这么几种:

  • 前向填充(ffill)——用上一个有效值填充。适合短时间缺失,比如几分钟的数据断点。
  • 后向填充(bfill)——用下一个有效值填充。和ffill类似,方向相反。
  • 线性插值——用前后两个点的平均值。我个人最常用这个,简单且稳定。
  • 多项式插值——用更高阶的曲线拟合。嗯,这里要注意,阶数太高容易过拟合,我一般不超过3阶。
  • 模型预测填充——用其他特征预测缺失值。比如用温度、风速来预测发电量。这个方法精度高,但计算量大。

举个例子,看看代码怎么写:

import pandas as pd
import numpy as np

# 模拟发电量数据
data = pd.Series([100, np.nan, np.nan, 120, 130, np.nan, 150])

# 前向填充
data_ffill = data.ffill()

# 线性插值
data_interp = data.interpolate(method='linear')

print("原始数据:", data.values)
print("前向填充:", data_ffill.values)
print("线性插值:", data_interp.values)

我的小技巧:对于发电量这种周期性很强的数据,可以试试按时间窗口分组插值。比如每天同一时刻的数据,用历史同期的均值来填充。我在一个光伏项目里用过,效果比普通插值好不少。

4.2 异常值检测

异常值是什么?说白了就是那些明显不合理的数据点。比如半夜12点发电量突然飙到满负荷,或者大晴天发电量突然掉到零。你想想看,这明显有问题。

我曾经在一个风电项目里遇到过:某台风机的发电量数据,连续三天都出现凌晨3点突然跳变。一开始以为是设备故障,查了半天才发现是数据采集卡接触不良。嗯,这就是典型的异常值问题。

常用的检测方法:

方法 适用场景 优缺点
3σ原则 数据近似正态分布 简单快速,但对非正态分布效果差
箱线图法(IQR) 任意分布 鲁棒性好,但可能漏掉局部异常
Z-score 数据量较大 标准化后便于比较,但受极端值影响
孤立森林 高维数据 适合复杂场景,但参数调起来麻烦

我建议你从箱线图法开始。为什么?因为它不依赖数据分布,而且直观好理解。看看代码:

def detect_outliers_iqr(data, factor=1.5):
    Q1 = data.quantile(0.25)
    Q3 = data.quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - factor * IQR
    upper_bound = Q3 + factor * IQR
    outliers = data[(data < lower_bound) | (data > upper_bound)]
    return outliers, lower_bound, upper_bound

# 示例
power_data = pd.Series([100, 102, 98, 105, 500, 101, 99, 103])
outliers, lb, ub = detect_outliers_iqr(power_data)
print(f"异常值: {outliers.values}")
print(f"正常范围: [{lb:.2f}, {ub:.2f}]")

注意:异常值不一定要直接删除。有时候异常值本身包含重要信息,比如设备故障的前兆信号。我建议你先标记出来,分析原因后再决定怎么处理。

4.3 数据平滑技术

发电量数据里总有些毛刺——不是异常值,就是正常的波动。比如风速突然变一下,光伏板被云遮了一下。这些毛刺会影响模型训练,让模型学到的不是趋势,而是噪声。

数据平滑,就是把这些毛刺磨平,让数据更干净。

常用的平滑方法:

  • 移动平均——最简单的平滑方法。窗口大小是关键,窗口太小平滑效果差,窗口太大又会丢失细节。我一般从5开始试。
  • 指数加权移动平均(EWMA)——给近期的数据更高权重。适合有趋势的数据。
  • Savitzky-Golay滤波——用多项式拟合局部数据。这个我特别喜欢,因为它能保留数据的形状特征。
  • 小波去噪——把信号分解到不同频率,去掉高频噪声。适合噪声频率和信号频率差异大的情况。

看看代码实现:

from scipy.signal import savgol_filter

# 模拟带噪声的发电量数据
np.random.seed(42)
time = np.arange(0, 100, 0.5)
power = 50 + 10 * np.sin(time * 0.1) + np.random.normal(0, 3, len(time))

# Savitzky-Golay滤波
power_smooth = savgol_filter(power, window_length=11, polyorder=3)

# 移动平均
window_size = 5
power_ma = np.convolve(power, np.ones(window_size)/window_size, mode='valid')

print("原始数据前10个点:", power[:10].round(2))
print("平滑后前10个点:", power_smooth[:10].round(2))

我的经验:平滑参数不要设得太激进。我曾经在一个项目里把窗口设得太大,结果把发电量的真实波动都磨没了,模型预测出来一条直线。后来我学乖了——先看数据频谱,再选平滑参数。

4.4 知识体系总览

说了这么多,咱们用一张图把整个数据清洗的流程串起来:

数据清洗知识体系 原始发电量数据 缺失值处理 异常值检测 数据平滑 前向/后向填充 线性/多项式插值 模型预测填充 3σ原则 / IQR Z-score / 孤立森林 标记后分析原因 移动平均 / EWMA Savitzky-Golay 小波去噪 干净可用的数据

这张图把咱们今天讲的内容都串起来了。从原始数据出发,经过缺失值处理、异常值检测、数据平滑三个步骤,最终得到干净可用的数据。每一步都有多种方法可选,具体用哪个,得看你的数据特点。

总结一下:数据清洗不是一次性工作,而是需要反复迭代的过程。我建议你先用简单方法快速处理,看看模型效果,再根据反馈调整清洗策略。别想着一步到位,那不太现实。

好了,今天就聊到这儿。数据清洗这块,说白了就是「垃圾进,垃圾出」——数据质量决定了模型的天花板。下一节咱们聊聊特征工程,那又是另一番天地了。

专注资料整理