第四章 特征工程:让数据开口说话
特征工程,说白了就是给模型喂“好料”。
我做了这么多年风电功率预测,最深的体会就是:模型再牛,也架不住特征太烂。你想想看,如果输入的都是噪音,输出能准吗?
这一章,我们就聊聊怎么从原始数据里挖出真正有用的特征。我个人习惯把这部分工作分成四大块:时间特征、气象特征、历史功率特征、滚动特征。咱们一个一个来。
4.1 时间特征:捕捉周期规律
风电功率有明显的日周期和季节周期。白天风大还是晚上风大?夏天和冬天风速分布一样吗?这些规律,得靠时间特征来告诉模型。
4.1.1 小时特征
小时特征不是简单地把“1-24”扔进去。我建议做正弦-余弦编码。为什么?因为23点和0点其实很近,但数值上差了很多。模型会以为它们很远。
# 小时的正弦-余弦编码
import numpy as np
hour = np.array([0, 1, 2, ..., 23])
hour_sin = np.sin(2 * np.pi * hour / 24)
hour_cos = np.cos(2 * np.pi * hour / 24)
这样处理后,23点和0点在二维空间里就是相邻的。嗯,这里要注意:所有周期性特征都建议这么处理,包括月份、星期几。
4.1.2 季节特征
季节对风电的影响太大了。我在北方某风电场做过项目,冬天平均风速比夏天高出30%以上。季节特征我一般用两种方式:
- 硬编码:直接标1-4,或者用one-hot编码
- 连续编码:用月份的正弦-余弦,这样季节过渡更平滑
我个人更倾向第二种。因为季节是渐变的过程,不是突然从春天跳到夏天的。
4.2 气象特征:核心驱动力
风速、风向、温度、气压、湿度,这五个是气象特征的核心。但直接扔进去?不行。得加工。
4.2.1 风速特征
风速是功率预测最重要的特征,没有之一。但要注意:风速和功率不是线性关系。我建议做以下处理:
- 风速的平方、立方:因为功率和风速的三次方近似成正比
- 风速的指数变换:比如取对数,让分布更接近正态
- 风速的差分:风速变化率,能反映天气系统的移动
# 风速特征扩展
wind_speed = df['wind_speed'].values
df['wind_speed_sq'] = wind_speed ** 2
df['wind_speed_cu'] = wind_speed ** 3
df['wind_speed_log'] = np.log1p(wind_speed) # 避免0值问题
df['wind_speed_diff'] = np.diff(wind_speed, prepend=wind_speed[0])
4.2.2 风向特征
风向也是周期特征,同样需要正弦-余弦编码。但这里有个坑:风向对功率的影响不是对称的。比如,有些风电场北风时功率高,南风时功率低,但东风和西风可能差不多。
我曾经遇到过一个项目,模型预测偏差很大。排查了很久,发现是风向编码的问题。后来我加了一个风向分段特征:把360度分成8个扇区,每个扇区一个二进制特征。效果立竿见影。
4.2.3 温度、气压、湿度
这三个特征相对简单,但也有一些门道:
| 特征 | 常见处理 | 我的经验 |
|---|---|---|
| 温度 | 标准化或归一化 | 注意昼夜温差大的地区,建议用滑动平均平滑 |
| 气压 | 直接使用或取差分 | 气压变化率比绝对值更有用,能指示天气系统变化 |
| 湿度 | 标准化 | 湿度高时往往风速低,这个交互特征值得关注 |
4.3 历史功率特征:让模型记住过去
风电功率有很强的自相关性。现在的功率和过去几分钟、几小时的功率高度相关。所以,历史功率特征是必加的。
4.3.1 滞后特征
滞后特征就是过去时刻的功率值。我一般会取以下滞后:
- 短时滞后:t-1, t-2, t-3(分钟级)
- 中时滞后:t-10, t-30(十分钟到半小时级)
- 长时滞后:t-60, t-120(小时级)
具体取哪些滞后,要看你的预测步长和数据频率。我习惯先做自相关分析,看看哪些滞后相关性高,再决定。
4.3.2 统计特征
除了原始值,我还会计算滑动窗口内的统计量:
# 滑动窗口统计特征
window = 10 # 过去10个时间步
df['power_mean_10'] = df['power'].rolling(window=window).mean()
df['power_std_10'] = df['power'].rolling(window=window).std()
df['power_max_10'] = df['power'].rolling(window=window).max()
df['power_min_10'] = df['power'].rolling(window=window).min()
这些统计特征能反映功率的波动情况。比如,标准差大说明功率不稳定,模型可能需要更保守的预测。
4.4 滚动特征:捕捉动态变化
滚动特征和滑动窗口统计有点像,但更强调变化趋势。我个人觉得这是特征工程里最容易被忽视的部分。
4.4.1 差分特征
一阶差分:当前值减去上一个值。二阶差分:一阶差分的差分。差分能反映变化速度。
df['power_diff_1'] = df['power'].diff(1)
df['power_diff_2'] = df['power'].diff(2)
df['wind_speed_diff_1'] = df['wind_speed'].diff(1)
4.4.2 比率特征
有时候相对变化比绝对变化更有意义。比如:
- 当前风速 / 过去1小时平均风速
- 当前功率 / 过去1小时平均功率
- 功率变化率:(当前功率 - 过去功率) / 过去功率
这些比率特征能消除量纲影响,让模型更关注相对变化。
4.4.3 趋势特征
我还会用线性回归拟合过去N个时间步的斜率,作为趋势特征。比如,过去10分钟风速是上升还是下降?上升速度有多快?
# 计算趋势斜率
from scipy import stats
def calc_trend(series):
x = np.arange(len(series))
slope, _, _, _, _ = stats.linregress(x, series)
return slope
df['power_trend_10'] = df['power'].rolling(window=10).apply(calc_trend)
4.5 特征工程知识体系
下面这张图,是我对本章知识体系的总结。你可以把它当作一个检查清单,做特征工程时对照着来。
好了,特征工程这部分就聊到这儿。记住:好的特征能让模型事半功倍。下一章我们聊聊特征选择,看看怎么从一堆特征里挑出最精华的部分。
公众号:蓝海资料掘金营,微信deep3321