3. 特征工程基础:时间特征提取、气象特征融合、滞后特征构造
各位同学,咱们今天聊聊特征工程。说实话,在风电预测这个领域,模型再牛,特征不行也是白搭。我见过太多人一上来就堆模型,结果精度上不去,回头一看,特征工程做得一塌糊涂。说白了,特征工程就是给模型喂“好料”,料不好,神仙也救不了。
这一章,我重点讲三个核心方向:时间特征提取、气象特征融合、滞后特征构造。这三个东西,是我在多个风电项目里反复打磨出来的“三板斧”。
核心观点:特征工程不是越多越好,而是越“对”越好。时间、气象、滞后,这三类特征基本覆盖了风电预测90%的信息量。
3.1 时间特征提取:给数据打上“时间标签”
风电功率跟时间的关系,比你想象的要紧密得多。白天风大还是晚上风大?夏天风多还是冬天风多?这些规律,模型自己学起来很费劲,但如果你把时间特征喂给它,它一下就懂了。
我个人习惯,时间特征至少做以下几类:
- 小时特征:0-23,表示一天中的时刻。注意,这里最好做周期编码,用sin和cos变换,避免0点和23点被模型当成“距离很远”。
- 星期特征:1-7,区分工作日和周末。风电负荷在周末往往偏低,这个特征很有用。
- 季节特征:春、夏、秋、冬,或者直接用月份。不同季节的风资源分布差异很大。
- 节假日标记:0/1二值特征。节假日期间,电网负荷变化,风电消纳能力也不同。
- 昼夜标记:白天/夜晚,或者日出日落时间。这个对光伏+风电混合场站特别重要。
小技巧:时间特征的周期编码,我一般用 sin(2π * hour / 24) 和 cos(2π * hour / 24) 两个维度。这样既保留了时间的顺序性,又解决了“0和23很近”的问题。我在一个项目中试过,加了周期编码后,模型精度提升了约3%。
# 时间特征周期编码示例
import numpy as np
import pandas as pd
def add_time_features(df, datetime_col='datetime'):
"""添加时间特征"""
df['hour'] = df[datetime_col].dt.hour
df['dayofweek'] = df[datetime_col].dt.dayofweek
df['month'] = df[datetime_col].dt.month
# 周期编码
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)
# 节假日标记(需要外部数据)
# df['is_holiday'] = df[datetime_col].isin(holiday_dates).astype(int)
return df
3.2 气象特征融合:别只盯着风速
很多人做风电预测,就只拿风速一个气象特征。嗯,这其实是个坑。风速当然重要,但气象要素之间是相互影响的。你想想看,风向变了,风速再大也可能没功率;温度低了,空气密度变大,同样风速下功率反而更高。
我在项目中遇到过一个案例:某风场在冬季经常出现“风速很大但功率上不去”的情况。后来一查,是气温骤降导致空气密度变化,而模型根本没考虑温度特征。加了温度特征后,预测误差直接降了5%。
我建议融合以下气象特征:
| 特征名称 | 说明 | 重要性 |
|---|---|---|
| 风速 | 平均风速、最大风速、最小风速 | ★★★★★ |
| 风向 | 角度值,建议做sin/cos编码 | ★★★★☆ |
| 温度 | 影响空气密度,进而影响功率 | ★★★★☆ |
| 气压 | 与风速、风向强相关 | ★★★☆☆ |
| 湿度 | 高湿度可能影响叶片结冰 | ★★★☆☆ |
| 湍流强度 | 反映风速波动剧烈程度 | ★★★☆☆ |
避坑指南:我曾经在融合气象数据时,直接用了数值天气预报的原始数据,没做空间插值。结果模型在风场边缘区域预测偏差很大。后来我改用双线性插值,把网格点数据插值到风机点位,效果才正常。记住:气象数据一定要做空间对齐。
3.3 滞后特征构造:让模型“记住”历史
风电功率有很强的自相关性。说白了,前一小时的功率,对预测下一小时很有帮助。滞后特征,就是把历史时刻的数据“搬”到当前时刻来用。
我建议构造以下几类滞后特征:
- 功率滞后:t-1, t-2, t-3, ..., t-24 时刻的功率值。注意,滞后阶数不要太多,否则特征维度爆炸。我一般选6-12个关键滞后点。
- 气象滞后:风速、风向等气象要素的滞后值。因为气象变化有惯性,前一小时的风速对当前有参考价值。
- 滑动统计量:过去N小时的均值、标准差、最大值、最小值。这些能反映短期趋势和波动性。
- 差分特征:当前值与前一时段的差值,反映变化趋势。
# 滞后特征构造示例
def add_lag_features(df, lag_hours=[1, 2, 3, 6, 12, 24]):
"""添加滞后特征"""
for lag in lag_hours:
df[f'power_lag_{lag}h'] = df['power'].shift(lag)
df[f'wind_speed_lag_{lag}h'] = df['wind_speed'].shift(lag)
# 滑动统计量(过去6小时)
df['power_rolling_mean_6h'] = df['power'].rolling(window=6).mean()
df['power_rolling_std_6h'] = df['power'].rolling(window=6).std()
# 差分特征
df['power_diff_1h'] = df['power'].diff(1)
# 注意:shift和rolling会产生NaN,需要填充或删除
df = df.dropna()
return df
关键提醒:构造滞后特征时,一定要注意数据泄露问题。比如你要预测t时刻的功率,就不能用t时刻之后的数据来构造特征。shift操作要确保方向正确,别把未来的数据“穿越”回来了。我曾经因为这个低级错误,模型在测试集上表现“完美”,一上线就崩了。
3.4 特征组合与筛选
特征做完了,别急着训练。我一般会做两件事:
- 特征组合:比如风速×风向、温度×湿度,这些组合特征有时能捕捉到单一特征无法表达的关系。
- 特征筛选:用相关性分析、特征重要性排序,把冗余特征去掉。特征太多反而容易过拟合。
举个例子,风速和功率的相关性很高,但如果你同时用了“风速”和“风速的平方”,这两个特征高度相关,模型会不稳定。我一般用方差膨胀因子(VIF)来检测多重共线性,VIF大于10的特征就考虑剔除。
# 特征筛选示例(使用随机森林特征重要性)
from sklearn.ensemble import RandomForestRegressor
def select_features(X, y, threshold=0.01):
"""基于特征重要性筛选"""
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X, y)
importances = pd.Series(model.feature_importances_, index=X.columns)
selected = importances[importances > threshold].index.tolist()
print(f"原始特征数: {len(X.columns)}")
print(f"筛选后特征数: {len(selected)}")
return X[selected]
好了,特征工程这块就讲这么多。时间特征、气象特征、滞后特征,这三样东西你做好了,模型精度至少能提10%-15%。别嫌麻烦,这一步值得花时间。