4、数据预处理(下):数据归一化/标准化、时间序列重采样、特征工程初步

好,咱们接着聊数据预处理的下半场。上一节我们把缺失值、异常值这些「脏东西」处理干净了,但数据还没法直接喂给模型。为什么?因为原始数据的「脾气」不一样——有的特征数值大,有的数值小,模型会「偏科」。

我刚开始做功率预测那会儿,就吃过这个亏。直接把原始数据扔进LSTM,结果模型训练半天不收敛,损失函数像过山车一样上下乱窜。后来才发现,是特征量纲没统一。嗯,这节咱们就把这个坑填上。

4.1 数据归一化与标准化:给数据「统一度量衡」

说白了,归一化和标准化就是把不同尺度的数据拉到同一个「频道」上。你想想看,风速可能是0~30 m/s,温度是-10~40℃,功率却是0~2000 kW。模型如果直接学,它会把注意力全放在数值大的特征上,忽略掉小数值特征的信息。

核心区别一句话:

  • 归一化(Min-Max Scaling):把数据压缩到[0,1]区间。适合数据分布有明确边界的情况,比如风速、辐照度。
  • 标准化(Z-score Normalization):把数据变成均值为0、标准差为1的分布。适合数据存在异常值或分布未知的情况,比如功率波动。

我在项目中遇到过这样一个场景:某光伏电站的功率数据偶尔会有负值(逆变器反送电),如果用归一化,负值会被映射到0以下,破坏[0,1]区间。这时候标准化就更合适,因为它不强制数据落在固定区间。

4.1.1 归一化(Min-Max Scaling)

公式很简单:

X_norm = (X - X_min) / (X_max - X_min)

代码实现:

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler(feature_range=(0, 1))
data_scaled = scaler.fit_transform(data.reshape(-1, 1))

我的习惯: 归一化之后,记得把scaler对象保存下来。预测阶段要用同样的scaler去反变换,把预测结果还原成真实功率值。我曾经忘了这步,预测结果全是0~1的小数,被领导问「你预测的是功率还是概率?」

4.1.2 标准化(Z-score Normalization)

公式:

X_std = (X - μ) / σ

代码实现:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
data_std = scaler.fit_transform(data.reshape(-1, 1))

注意: 标准化不保证数据落在某个固定区间。如果你的模型对输入范围敏感(比如Sigmoid激活函数),标准化后可能仍有部分数据落在[-3,3]之外。这时候归一化可能更稳妥。

4.2 时间序列重采样:对齐数据的「时间步调」

功率预测的数据来源五花八门。气象数据可能是每10分钟一个点,功率数据是每5分钟一个点,辐照度数据甚至是每1分钟一个点。时间戳对不齐,模型就没法学。

重采样,说白了就是把不同频率的数据统一到同一个时间间隔上。我个人习惯用15分钟或1小时作为标准间隔,具体看预测任务的需求——短期预测(未来4小时)用15分钟间隔,超短期预测(未来1小时)用5分钟间隔。

4.2.1 降采样(Downsampling)

把高频数据变成低频数据。比如把1分钟数据聚合成15分钟数据。

import pandas as pd

# 假设df是1分钟频率的数据
df_resampled = df.resample('15T').mean()  # '15T'表示15分钟,用均值聚合

聚合方式怎么选?我一般这样判断:

  • 功率数据:用均值。因为功率是连续变化的,均值能代表这15分钟的整体水平。
  • 风速数据:用均值或最大值。如果关注阵风影响,最大值更有意义。
  • 辐照度数据:用均值。辐照度变化相对平滑,均值足够。

4.2.2 升采样(Upsampling)

把低频数据变成高频数据。比如把1小时数据插值成15分钟数据。

# 线性插值
df_resampled = df.resample('15T').interpolate(method='linear')

避坑指南: 我曾经把气象数据从1小时升采样到15分钟,直接用线性插值。结果模型预测效果很差,后来发现是因为气象变化不是线性的——比如温度在日出前后变化很快,线性插值会「平滑」掉这种突变。后来我改用三次样条插值,效果好了不少。

4.3 特征工程初步:从原始数据中「挖金子」

数据对齐好了,但原始特征往往不够用。你想想看,模型只看到当前时刻的风速和功率,它怎么知道接下来是上升趋势还是下降趋势?所以我们要手动构造一些「有信息量」的特征。

特征工程是个大话题,这里我们先聊几个最常用的「入门级」操作。

4.3.1 时间特征提取

从时间戳里提取出:

  • 小时:0~23,反映日周期
  • 星期几:0~6,反映周周期(光伏周末可能负荷低)
  • 月份:1~12,反映季节变化
  • 是否节假日:0或1,这个对负荷预测特别重要
df['hour'] = df.index.hour
df['dayofweek'] = df.index.dayofweek
df['month'] = df.index.month
df['is_holiday'] = df.index.isin(holiday_dates).astype(int)

4.3.2 滞后特征(Lag Features)

把过去时刻的值作为当前时刻的特征。比如用t-1时刻的功率预测t时刻的功率。

df['power_lag_1'] = df['power'].shift(1)
df['power_lag_2'] = df['power'].shift(2)
df['power_lag_3'] = df['power'].shift(3)

我的经验: 滞后阶数不要太多。对于15分钟间隔的数据,滞后3~6阶(对应45分钟~1.5小时)通常就够用了。太多滞后特征会让模型「记性太好」,反而忽略其他特征的作用。

4.3.3 滑动窗口统计特征

计算过去一段时间窗口内的统计量,比如均值、标准差、最大值、最小值。

df['power_rolling_mean_3'] = df['power'].rolling(window=3).mean()
df['power_rolling_std_3'] = df['power'].rolling(window=3).std()
df['power_rolling_max_3'] = df['power'].rolling(window=3).max()

窗口大小怎么选?我一般遵循「预测步长×2」的原则。比如预测未来1小时(4个15分钟步长),窗口就选8个点(2小时)。这样模型能看到足够长的历史趋势。

4.4 本章知识体系总览

下面这张图把咱们这节的核心逻辑串起来了。你可以把它当作一个「检查清单」——做数据预处理时,按这个流程走一遍,基本不会漏东西。

数据预处理(下)知识体系 原始数据 归一化 / 标准化 时间序列重采样 特征工程初步 Min-Max Z-score 降采样 升采样 时间特征 滞后特征 滑动窗口 标准化后的干净数据 下一步:输入模型训练 ⚠️ 注意保存scaler ⚠️ 注意窗口大小

嗯,到这步,数据预处理的核心工作就差不多了。归一化/标准化解决了量纲问题,重采样对齐了时间频率,特征工程初步给模型「喂」了更有营养的信息。下一节我们会继续深入特征工程,聊聊怎么用更高级的方法从数据里挖出更多「金子」。

一句话总结: 数据预处理不是「做完就行」,而是「做对才行」。归一化选Min-Max还是Z-score?重采样用均值还是最大值?滞后特征取几阶?这些选择直接影响模型效果。我的建议是:多试几组参数,用验证集效果说话。


公众号:蓝海资料掘金营,微信deep3321