特征工程基础:时间特征、滞后特征与滑动窗口
各位同学好,我是你们的老朋友。今天咱们聊聊电价预测里最基础、也最容易被忽视的一环——特征工程。
说实话,我见过太多人一上来就堆模型,结果效果惨不忍睹。为什么?因为特征没做好。你想想看,电价这东西,跟时间的关系太密切了。早上用电高峰、周末工厂停工、夏天开空调……这些信息你不提取出来,模型再强也白搭。
一、时间特征:把时钟拆开看
时间特征,说白了就是把一个时间戳拆成多个维度。我个人习惯至少做以下几类:
1. 小时特征
这个最直观。电价在一天内波动很大,早高峰、晚高峰、低谷时段都不一样。我建议直接提取小时数(0-23),但要注意——模型可能把它当成连续变量。所以,我通常会做正弦-余弦编码:
# 小时的正弦-余弦编码
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
为什么要这么干?因为23点和0点其实很近,但数值上差23。正弦余弦编码能保留这种循环关系。我在做德国电价预测项目时,光这一步就提升了3%的精度。
2. 星期特征
工作日和周末的电价模式完全不同。我一般提取星期几(0-6),然后做独热编码。但注意——周一和周五虽然都是工作日,但电价行为有差异。周五下午往往有周末效应,电价会提前走低。
重要提示: 如果你做的是短期预测(比如未来24小时),星期特征可能不够细。我建议再加一个「是否周末」的二元特征,简单粗暴但有效。
3. 季节特征
电价有明显的季节性。夏天制冷、冬天取暖,用电量差异很大。我习惯用月份或季度,同样做正弦-余弦编码:
# 月份的正弦-余弦编码
df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)
嗯,这里要注意——如果你在赤道附近做项目,季节特征可能不明显。但咱们做电价预测,大部分场景都在温带地区,这个特征很管用。
二、滞后特征:让模型记住过去
电价预测本质上是个时序问题。今天的电价跟昨天、前天甚至上周同一天都有关系。滞后特征就是把这些历史值拿过来当特征。
我一般会做以下几类滞后:
- 短期滞后: lag-1, lag-2, lag-3(前1、2、3小时)
- 日周期滞后: lag-24, lag-48(昨天同一时刻、前天同一时刻)
- 周周期滞后: lag-168(上周同一时刻,如果数据是小时级)
# 创建滞后特征
df['lag_1'] = df['price'].shift(1)
df['lag_24'] = df['price'].shift(24)
df['lag_168'] = df['price'].shift(168)
避坑指南: 我曾经犯过一个低级错误——直接用shift创建滞后特征,结果训练集和测试集混在一起了。记住:一定要在划分训练集/测试集之后再做滞后,否则会造成数据泄露。
另外,滞后阶数不是越多越好。我见过有人做50阶滞后,结果模型过拟合得一塌糊涂。我的经验是:先做相关性分析,看看哪些滞后跟目标变量相关度高,再决定用哪些。
三、滑动窗口统计:捕捉趋势和波动
滞后特征只能看到某个时间点的值,但电价的变化趋势也很重要。滑动窗口统计就是计算过去一段时间内的统计量,比如均值、标准差、最大值、最小值等。
我常用的窗口大小:
| 窗口 | 统计量 | 作用 |
|---|---|---|
| 3小时 | 均值、标准差 | 捕捉短期趋势和波动 |
| 6小时 | 均值、最大值 | 反映半天的用电模式 |
| 24小时 | 均值、最小值、最大值 | 日周期内的整体情况 |
| 168小时(一周) | 均值、标准差 | 周周期趋势 |
# 滑动窗口统计
df['rolling_mean_3h'] = df['price'].rolling(window=3).mean()
df['rolling_std_6h'] = df['price'].rolling(window=6).std()
df['rolling_max_24h'] = df['price'].rolling(window=24).max()
你想想看,如果过去3小时电价一直在涨,那接下来继续涨的概率就很大。滑动窗口均值就能捕捉这种趋势。而标准差能告诉你市场波动大不大——波动大的时候,预测难度也大。
小技巧: 滑动窗口会产生NaN值(前几个时间点没有足够的历史数据)。我一般会直接删除这些行,或者用前向填充。千万别用0填充,那会严重扭曲模型。
四、节假日特征:别让模型蒙在鼓里
节假日对电价的影响非常大。春节、国庆、圣诞节……这些日子工厂停工、商业活动减少,用电量骤降。如果不告诉模型,它就会把节假日当成普通工作日来预测,结果可想而知。
我一般做两类节假日特征:
- 是否节假日: 二元特征,1表示节假日,0表示非节假日
- 节假日前/后: 比如节前1天、节后1天,这些过渡日的电价行为也很特殊
# 节假日特征
df['is_holiday'] = df['date'].isin(holiday_list).astype(int)
df['days_to_holiday'] = (df['date'] - next_holiday).dt.days # 距离下一个节假日的天数
我曾经在做一个欧洲电价项目时,发现模型在圣诞节前后误差特别大。后来一查,原来我只加了「是否节假日」这个特征,但没考虑节前节后的过渡效应。加上「距离节假日的天数」后,误差直接降了一半。
核心要点: 节假日特征不是简单的0/1就完事了。要考虑节前效应、节后效应,甚至不同节假日的用电模式差异。比如春节和国庆,虽然都是长假,但用电行为完全不同。
五、知识体系总览
说了这么多,咱们用一张图来总结一下本章的核心内容:
这张图把咱们今天讲的内容串起来了。你看,四个大类下面各有子项,每个子项都有它的应用场景。做特征工程时,我建议你先把这些基础特征都做出来,然后用特征重要性分析或者相关性分析去筛选,别一股脑全塞给模型。
好了,特征工程的基础部分就讲到这里。记住一句话:好的特征能让简单的模型表现优秀,烂的特征能让复杂的模型一败涂地。咱们下节课见。