第1章:特征工程(上)——时间特征提取、统计特征、滞后特征与滑动窗口
各位好,我是老张。在风电行业摸爬滚打了十几年,今天咱们聊聊特征工程。
说实话,很多刚入行的朋友问我:「模型预测不准怎么办?」我通常反问一句:「你的特征做干净了吗?」
特征工程,说白了就是给模型喂「好料」。你喂的是精饲料,它就能干活;你喂的是草料,它就给你摆烂。今天这一章,咱们把时间特征、统计特征、滞后特征和滑动窗口这几个核心招式讲透。
核心观点:特征工程决定了模型的上限,算法只是逼近这个上限。我见过太多项目,模型调参调了三个月,不如花三天做特征工程效果来得明显。
1.1 时间特征提取:让模型「看懂」时间
风电数据天然带有时间属性。风速、功率、温度,这些量都随时间变化。但模型不认识「2025-03-15 14:30:00」这种字符串,你得帮它翻译成数字。
小时特征——这个最简单,也最容易被忽略。
我习惯把小时拆成两路:
- 小时数值:0~23,直接喂给模型
- 小时正弦/余弦编码:因为23点和0点其实是相邻的,但数值上差23。用sin/cos编码可以保留这种周期性
# 小时的正弦余弦编码示例
import numpy as np
hour = np.array([0, 6, 12, 18, 23])
hour_sin = np.sin(2 * np.pi * hour / 24)
hour_cos = np.cos(2 * np.pi * hour / 24)
print("小时:", hour)
print("正弦:", hour_sin)
print("余弦:", hour_cos)
我的经验:在西北某风场,我们只用小时数值做特征,模型在凌晨0点和午夜23点附近总是预测偏差大。加上正弦余弦编码后,误差直接降了12%。
季节特征——风电的季节性非常明显。
春天风大,夏天风小,秋天过渡,冬天风硬。我一般这样处理:
- 春季(3-5月):编码为0
- 夏季(6-8月):编码为1
- 秋季(9-11月):编码为2
- 冬季(12-2月):编码为3
同样,也可以用正弦余弦编码处理季节的周期性。
月份特征——比季节更细粒度。
1~12月,直接编码。但要注意:12月和1月是相邻的,数值上差11。所以同样建议做正弦余弦变换。
| 特征名称 | 编码方式 | 取值范围 | 适用场景 |
|---|---|---|---|
| 小时 | 数值 + sin/cos | 0~23 | 日内波动预测 |
| 季节 | 数值 + sin/cos | 0~3 | 季节性功率预测 |
| 月份 | 数值 + sin/cos | 1~12 | 月度发电量估算 |
1.2 统计特征:用「数学语言」描述数据
统计特征,说白了就是给数据算「账本」。我经常跟团队说:别光看原始数据,你得算算它的「脾气」。
均值——反映数据的「中心」。
比如过去1小时的平均风速,能告诉你当前风况的「基调」。
方差——反映数据的「波动程度」。
方差大,说明风忽大忽小,发电不稳定。方差小,说明风比较平稳。我在项目中遇到过,方差大的时段,模型预测误差普遍偏高。
最大值和最小值——反映数据的「边界」。
过去10分钟的最大风速,能告诉你有没有阵风。最小值则告诉你有没有「断风」的风险。
# 统计特征计算示例
import pandas as pd
# 假设df是包含风速数据的DataFrame
df['wind_speed_mean'] = df['wind_speed'].rolling(window=10).mean()
df['wind_speed_std'] = df['wind_speed'].rolling(window=10).std()
df['wind_speed_max'] = df['wind_speed'].rolling(window=10).max()
df['wind_speed_min'] = df['wind_speed'].rolling(window=10).min()
注意:计算滑动窗口统计量时,窗口大小很关键。窗口太小,噪声大;窗口太大,反应迟钝。我一般从10分钟、30分钟、1小时三个尺度试,哪个效果好就用哪个。
1.3 滞后特征:让模型「记住」过去
风电预测有个特点:现在的风速和几分钟前的风速高度相关。这就是自相关性。
滞后特征,就是把过去时刻的值作为当前时刻的特征。比如:
- 滞后1步:t-1时刻的风速
- 滞后2步:t-2时刻的风速
- 滞后3步:t-3时刻的风速
我一般会做滞后1步、2步、3步、6步、12步、24步。为什么?因为不同时间尺度的记忆对模型都有用。
# 滞后特征生成
df['lag_1'] = df['wind_speed'].shift(1)
df['lag_2'] = df['wind_speed'].shift(2)
df['lag_3'] = df['wind_speed'].shift(3)
df['lag_6'] = df['wind_speed'].shift(6)
df['lag_12'] = df['wind_speed'].shift(12)
df['lag_24'] = df['wind_speed'].shift(24)
避坑指南:我曾经在某个项目中,把滞后特征做到50步,结果模型过拟合得一塌糊涂。后来发现,滞后步数太多,模型记住了太多「历史噪音」。一般来说,滞后步数不超过数据采样频率的2倍比较稳妥。
1.4 滑动窗口特征:捕捉「趋势」
滑动窗口和滞后特征有点像,但思路不同。滞后特征是「点」,滑动窗口是「段」。
滑动窗口特征,就是在一个固定大小的窗口内,计算各种统计量。比如:
- 过去10分钟的均值(趋势)
- 过去10分钟的标准差(波动)
- 过去10分钟的最大值(峰值)
- 过去10分钟的最小值(谷值)
- 过去10分钟的变化率(斜率)
我习惯用多个窗口大小:10分钟、30分钟、1小时、2小时。不同窗口捕捉不同时间尺度的模式。
# 多窗口滑动特征
for window in [10, 30, 60, 120]:
df[f'mean_{window}'] = df['wind_speed'].rolling(window=window).mean()
df[f'std_{window}'] = df['wind_speed'].rolling(window=window).std()
df[f'max_{window}'] = df['wind_speed'].rolling(window=window).max()
df[f'min_{window}'] = df['wind_speed'].rolling(window=window).min()
核心逻辑:滑动窗口特征的本质,是把「时间序列」转化为「统计分布」。模型不再只看一个点,而是看一段历史。这就像你看一个人,不光看他一瞬间的表情,还要看他过去几分钟的情绪变化——这样判断才准。
1.5 本章知识体系总览
下面这张图,是我自己画的特征工程框架。每次做项目前,我都会对照这张图检查一遍,看有没有遗漏。
嗯,这张图基本概括了本章的核心内容。你想想看,时间特征让模型知道「现在是什么时候」,统计特征告诉模型「数据长什么样」,滞后特征让模型「记住过去」,滑动窗口让模型「看到趋势」。四者结合,模型才能「看懂」风电数据。
最后说一句:特征工程没有标准答案。每个风场、每台机组都有自己的「脾气」。我建议你拿到数据后,先花一周时间做特征探索,再花一天时间建模。顺序别搞反了——我见过太多人上来就调参,结果调了个寂寞。