数据预处理实战:缺失值处理、异常值检测与修正、数据归一化与标准化
大家好,我是你们的老朋友。今天咱们来聊聊数据预处理。
说实话,我在风电预测这行摸爬滚打这么多年,见过太多人一上来就调模型、跑算法。结果呢?数据质量不行,模型再花哨也是白搭。我自己的经验是,数据预处理至少占整个项目60%的时间。你想想看,风电场的数据有多脏?传感器故障、通信中断、极端天气干扰……嗯,今天我就把这些坑一个个给你填上。
核心观点:数据预处理不是“体力活”,而是决定模型上限的关键一步。数据干净了,模型自然就准了。
一、缺失值处理:别让“空洞”毁了你的模型
风电数据里,缺失值太常见了。我记得有一次做某风场的项目,风速传感器在冬季连续三天没数据,因为冻住了。你想想,三天啊,直接扔掉?那模型还学个啥?
处理缺失值,我一般分三步走:
- 先诊断:看看缺失是随机的,还是系统性的。比如某个传感器总是半夜掉线,那就是硬件问题,得修。
- 再选择方法:缺失率低于5%,我习惯直接删除;5%-20%,用插值;超过20%,就得结合业务逻辑了。
- 最后验证:处理完一定要对比前后分布,别把数据搞偏了。
下面是我常用的几种插值方法,直接上代码:
import pandas as pd
import numpy as np
# 生成示例数据(风速序列)
np.random.seed(42)
data = {
'timestamp': pd.date_range('2024-01-01', periods=100, freq='H'),
'wind_speed': np.random.rand(100) * 15 + 3
}
df = pd.DataFrame(data)
# 人为制造缺失值
df.loc[20:25, 'wind_speed'] = np.nan
df.loc[50:55, 'wind_speed'] = np.nan
# 方法1:线性插值(我最常用的)
df['wind_speed_linear'] = df['wind_speed'].interpolate(method='linear')
# 方法2:时间加权插值(适合风速这种有趋势的数据)
df['wind_speed_time'] = df['wind_speed'].interpolate(method='time')
# 方法3:前向填充(适合短时间缺失)
df['wind_speed_ffill'] = df['wind_speed'].fillna(method='ffill', limit=3)
print(df.head(10))
我的小技巧:对于风速、功率这种有物理规律的数据,我偏爱时间加权插值。因为它会考虑时间间隔,比线性插值更贴近真实变化。我曾经用这个方法,把一个项目的预测误差从8%降到了6.5%。
二、异常值检测与修正:揪出那些“捣乱分子”
异常值比缺失值更隐蔽。你想想,一个风速数据突然跳到50m/s,明显不合理,但模型可不知道。它会傻乎乎地学进去,结果预测就偏了。
我一般用两种方法检测异常值:
- 统计法:3σ原则、箱线图。适合数据分布比较规整的情况。
- 物理约束法:风速不能超过切出风速,功率不能超过额定功率。这个更靠谱。
举个例子,某风场额定功率是2MW,但数据里出现了2.5MW。这明显是传感器漂移了。我的处理方式是:
# 异常值检测与修正示例
import numpy as np
import pandas as pd
# 模拟数据
np.random.seed(42)
wind_speed = np.random.rand(100) * 25 # 0-25 m/s
power = np.where(wind_speed < 3, 0,
np.where(wind_speed < 12, (wind_speed - 3) / 9 * 2000,
np.where(wind_speed < 25, 2000, 0)))
# 加入异常值
power[30] = 2500 # 超过额定功率
power[60] = -100 # 负功率,不合理
# 物理约束检测
def detect_anomalies(power, rated_power=2000):
anomalies = []
for i, p in enumerate(power):
if p < 0 or p > rated_power * 1.1: # 允许10%的测量误差
anomalies.append(i)
return anomalies
anomaly_idx = detect_anomalies(power)
print(f"检测到异常值索引:{anomaly_idx}")
# 修正:用前后均值替换
for idx in anomaly_idx:
if idx > 0 and idx < len(power) - 1:
power[idx] = (power[idx-1] + power[idx+1]) / 2
else:
power[idx] = np.median(power) # 边界值用中位数
print(f"修正后的功率值:{power[anomaly_idx]}")
避坑指南:我曾经犯过一个错误——把所有超出3σ的数据都删了。结果发现,那些“异常值”其实是台风过境时的真实数据。所以,一定要结合业务场景判断。异常≠错误,可能是极端事件。
三、数据归一化与标准化:让所有特征“平起平坐”
这一步很多人觉得简单,不就是减均值除方差吗?但这里有个细节:风电数据里,风速和功率的量级差很多。风速是0-30,功率是0-2000。如果不归一化,模型会天然地更关注功率,因为它的数值大。
我常用的两种方法:
| 方法 | 公式 | 适用场景 | 我的经验 |
|---|---|---|---|
| Min-Max归一化 | (x - min) / (max - min) | 数据有明确边界(如功率0-2000) | 适合神经网络,收敛快 |
| Z-score标准化 | (x - mean) / std | 数据分布近似正态,或有异常值 | 适合线性模型、SVM |
直接上代码,看看实际效果:
from sklearn.preprocessing import MinMaxScaler, StandardScaler
import numpy as np
# 模拟风电数据
data = np.array([[5, 500], [10, 1200], [15, 1800], [20, 2000], [25, 2000]])
# Min-Max归一化
scaler_minmax = MinMaxScaler()
data_minmax = scaler_minmax.fit_transform(data)
print("Min-Max归一化结果:")
print(data_minmax)
# Z-score标准化
scaler_std = StandardScaler()
data_std = scaler_std.fit_transform(data)
print("\nZ-score标准化结果:")
print(data_std)
# 重点:预测时要用同样的scaler
# 保存scaler对象,测试集用transform,不要重新fit
import joblib
joblib.dump(scaler_minmax, 'scaler_minmax.pkl')
我的习惯:对于风电功率预测,我通常对风速做Z-score标准化,对功率做Min-Max归一化。为什么?因为风速分布更接近正态,而功率有明确的物理上下界。你试试看,效果会不一样。
四、知识体系总览
说了这么多,咱们用一张图把整个流程串起来。这是我做项目时必画的流程图,帮你理清思路:
这张图就是我每次做数据预处理时的“作战地图”。从原始数据开始,一步步走到干净数据集。每一步都有对应的技术和注意事项。你照着这个流程走,基本不会出大问题。
五、实战中的几个关键点
最后,我再唠叨几句实战中的细节:
- 顺序很重要:先处理缺失值,再做异常值检测。因为缺失值会影响异常值判断。
- 保存参数:归一化/标准化的参数(min、max、mean、std)一定要保存下来。测试集和预测时要用同样的参数,不能重新计算。
- 可视化验证:处理完数据后,画个分布图看看。如果分布形状变了,说明处理方式有问题。
总结一下:数据预处理不是一锤子买卖。我每次拿到新数据,都会先跑一遍这个流程。虽然看起来繁琐,但磨刀不误砍柴工。数据干净了,模型精度自然就上去了。嗯,今天就到这里,大家动手试试吧。