3、特征工程(上):日历特征、滞后特征与滑动窗口特征
各位同学,咱们今天聊点实在的。
模型选好了,数据也洗干净了,接下来干什么?
特征工程。
说白了,就是帮模型“开窍”。你给模型一堆原始负荷数据,它看不懂哪天是周末,也不知道昨天负荷高意味着什么。你得把这些“潜规则”显式地喂给它。
我个人习惯把特征工程分成三块:日历特征、滞后特征和滑动窗口特征。今天咱们先啃前两个,再加一个滑动窗口。
核心观点:负荷预测的本质是“模式识别”。日历特征告诉模型“什么时候”,滞后特征告诉模型“刚发生了什么”,滑动窗口告诉模型“最近一段时间的趋势”。三者缺一不可。
3.1 日历特征:给模型装上“日历”
你想想看,如果让你预测明天下午3点的负荷,你第一反应是什么?
是不是先看明天是周几?如果是周一,负荷通常偏高;如果是周日,负荷偏低。这就是日历特征的作用。
我建议至少构造以下三类日历特征:
- 小时特征(Hour of Day):0-23的数值。但注意,模型可能把它当成连续变量。我个人习惯用
sin/cos编码,或者直接做One-Hot。 - 星期特征(Day of Week):0-6。周一和周日差别巨大,但周二和周三差别不大。你可以考虑把周末单独拎出来。
- 节假日特征(Holiday Flag):0/1二值变量。我在项目中遇到过,春节那几天负荷直接腰斩,如果不加这个特征,模型会懵掉。
小技巧:对于小时特征,我常用sin(2π*hour/24)和cos(2π*hour/24)两个维度。这样23点和0点之间的距离是连续的,模型不会觉得它们“隔了一个世界”。
3.2 滞后特征:让模型记住“昨天”
负荷数据有个特点:惯性。
今天的负荷和昨天的负荷高度相关。尤其是对于15分钟粒度的数据,前一个时刻的负荷几乎决定了后一个时刻的负荷。
滞后特征,就是把过去某个时刻的值,直接拿过来当特征。
常用的滞后阶数:
| 滞后阶数 | 含义 | 适用场景 |
|---|---|---|
| Lag-1 | 上一个时刻的负荷 | 捕捉瞬时惯性 |
| Lag-24 | 昨天同一时刻的负荷 | 捕捉日周期性 |
| Lag-168 | 上周同一时刻的负荷 | 捕捉周周期性 |
避坑指南:我曾经在做一个短期预测项目时,一口气加了Lag-1到Lag-10,结果模型过拟合得一塌糊涂。后来发现,对于15分钟粒度的数据,Lag-1到Lag-4就够了。加太多反而引入噪声。
代码实现其实很简单。假设你的DataFrame叫df,负荷列叫load:
# 构造滞后特征
df['lag_1'] = df['load'].shift(1)
df['lag_24'] = df['load'].shift(24)
df['lag_168'] = df['load'].shift(168)
# 注意:shift之后会产生NaN,记得用fillna处理
df.fillna(method='bfill', inplace=True)
3.3 滑动窗口特征:看“趋势”而不是“点”
滞后特征只给了模型一个“点”的信息。但很多时候,我们需要的是“一段区间”的趋势。
比如,过去1小时的平均负荷是上升还是下降?这比单纯看上一个点更有价值。
滑动窗口特征,就是对过去N个时刻的值做统计计算。最常用的是滑动平均(Rolling Mean)和滑动标准差(Rolling Std)。
我常用的窗口大小:
- 1小时窗口(4个点,针对15分钟数据):捕捉短期波动
- 3小时窗口(12个点):捕捉中期趋势
- 24小时窗口(96个点):捕捉日周期趋势
嗯,这里要注意:窗口越大,特征越平滑,但也会丢失细节。你需要根据预测目标来权衡。
# 构造滑动窗口特征
df['rolling_mean_4'] = df['load'].rolling(window=4).mean()
df['rolling_std_4'] = df['load'].rolling(window=4).std()
df['rolling_mean_12'] = df['load'].rolling(window=12).mean()
# 同样,前几个值会是NaN
df.dropna(inplace=True)
实战经验:我做过一个对比实验——只用滞后特征 vs 滞后+滑动窗口。结果后者在RMSE上降低了约8%。原因很简单:滑动窗口相当于给模型加了一个“低通滤波器”,去掉了高频噪声,让模型更关注趋势。
3.4 三者如何组合?
你可能会问:这三个特征怎么搭配?
我的习惯是:日历特征打底,滞后特征抓细节,滑动窗口抓趋势。
举个例子,对于预测明天上午10点的负荷:
- 日历特征告诉模型:这是周二上午,属于工作日高峰时段
- Lag-24告诉模型:昨天上午10点的负荷是多少
- Rolling Mean告诉模型:过去3小时负荷是在涨还是在跌
三个特征一组合,模型基本就能猜个八九不离十。
最后一个小建议:特征不是越多越好。我见过有人一口气造了50个特征,结果模型训练时间翻了三倍,精度只提升了0.5%。先加核心特征,再逐步尝试,这才是正道。