特征工程基础:时间特征、滞后特征与滑动窗口

各位同学好,我是你们的老朋友。今天咱们聊聊电价预测里最基础、也最容易被忽视的一环——特征工程。

说实话,我见过太多人一上来就堆模型,结果效果惨不忍睹。为什么?因为特征没做好。你想想看,电价这东西,跟时间的关系太密切了。早上用电高峰、周末工厂停工、夏天开空调……这些信息你不提取出来,模型再强也白搭。

一、时间特征:把时钟拆开看

时间特征,说白了就是把一个时间戳拆成多个维度。我个人习惯至少做以下几类:

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就完事了。要考虑节前效应、节后效应,甚至不同节假日的用电模式差异。比如春节和国庆,虽然都是长假,但用电行为完全不同。

五、知识体系总览

说了这么多,咱们用一张图来总结一下本章的核心内容:

电价预测特征工程知识体系 特征工程 时间特征 滞后特征 滑动窗口统计 节假日特征 小时(正弦-余弦) 星期(独热编码) 季节(正弦-余弦) 短期滞后(1-3h) 日周期滞后(24h) 周周期滞后(168h) 3小时均值/标准差 6小时均值/最大值 24小时均值/极值 是否节假日(0/1) 节前/节后效应 距离节假日天数 核心原则:特征不是越多越好,要结合业务理解做筛选 避免数据泄露,注意训练/测试集分离

这张图把咱们今天讲的内容串起来了。你看,四个大类下面各有子项,每个子项都有它的应用场景。做特征工程时,我建议你先把这些基础特征都做出来,然后用特征重要性分析或者相关性分析去筛选,别一股脑全塞给模型。

好了,特征工程的基础部分就讲到这里。记住一句话:好的特征能让简单的模型表现优秀,烂的特征能让复杂的模型一败涂地。咱们下节课见。

专注资料整理