4、特征工程入门:时间特征提取

各位同学好,今天我们聊聊特征工程里最基础、也最容易被忽视的一块——时间特征。说实话,我刚入行那会儿,觉得直接把时间戳丢给模型就完事了。结果呢?模型训练出来,预测效果惨不忍睹。后来我才明白,时间这个维度,你得把它「拆开揉碎」了,模型才能看懂。

我个人习惯把时间特征分成三大类:周期性特征滞后特征统计特征。咱们一个一个说。

4.1 时间周期性特征:小时、季节、月份

风电功率有个明显的特点——它跟时间强相关。白天风大还是晚上风大?夏天风多还是冬天风多?这些规律,模型得学。

但时间戳本身是个线性增长的数值,比如"2024-03-15 14:30:00",模型很难直接理解它的周期性。所以我们要做的是:把时间拆成多个维度

核心思路:将时间戳分解为小时、星期、月份、季节等特征,并用正弦/余弦编码保留其周期性。

举个例子,小时特征。如果你直接用0-23的数值,模型会认为23和0离得很远,但实际上它们只差1小时。怎么办?用三角函数编码:

import numpy as np
import pandas as pd

def encode_hour(hour):
    """将小时编码为正弦和余弦特征"""
    hour_sin = np.sin(2 * np.pi * hour / 24)
    hour_cos = np.cos(2 * np.pi * hour / 24)
    return hour_sin, hour_cos

# 示例
df['hour_sin'] = df['hour'].apply(lambda x: encode_hour(x)[0])
df['hour_cos'] = df['hour'].apply(lambda x: encode_hour(x)[1])

同样的方法也适用于月份和季节。月份用12做周期,季节用4做周期。这样模型就能学到"凌晨和深夜其实很像"这种规律。

我的小技巧:除了正弦余弦,我还会额外加一个"是否节假日"的0/1特征。风电功率在节假日和普通工作日,用电负荷差异很大,这个特征往往能带来2-3%的精度提升。

4.2 滞后特征:让模型看到"昨天"

风电功率预测有个常识:现在的功率和过去几小时的功率高度相关。说白了,风不会突然停,也不会突然刮起来。所以我们要把过去时刻的功率值,作为当前时刻的特征。

这就是滞后特征(Lag Features)。

def create_lag_features(df, col='power', lags=[1, 2, 3, 6, 12, 24]):
    """创建滞后特征"""
    for lag in lags:
        df[f'lag_{lag}h'] = df[col].shift(lag)
    return df

# 注意:shift之后会有NaN值,记得处理
df = create_lag_features(df, lags=[1, 2, 3, 6, 12, 24])
df = df.dropna()  # 或者用fillna填充

我曾经踩过的坑:滞后特征用多了,模型会过度依赖历史值。比如你用了24小时的滞后,模型可能直接复制昨天的功率曲线,遇到天气突变就完全失效。我建议滞后阶数不要超过12小时,同时配合其他特征一起使用。

4.3 滑动窗口统计:捕捉趋势和波动

滞后特征只能看到某个时刻的值,但看不到一段时间的趋势。比如过去3小时功率是持续上升还是下降?波动大不大?这些信息对预测很有帮助。

滑动窗口统计就是干这个的。常用的统计量包括:

  • 均值:反映一段时间的平均水平
  • 标准差:反映波动程度
  • 最大值/最小值:反映极端情况
  • 斜率:反映变化趋势
def create_rolling_features(df, col='power', windows=[3, 6, 12]):
    """创建滑动窗口统计特征"""
    for window in windows:
        df[f'rolling_mean_{window}h'] = df[col].rolling(window).mean()
        df[f'rolling_std_{window}h'] = df[col].rolling(window).std()
        df[f'rolling_max_{window}h'] = df[col].rolling(window).max()
        df[f'rolling_min_{window}h'] = df[col].rolling(window).min()
    return df

df = create_rolling_features(df, windows=[3, 6, 12])

你想想看,如果过去3小时功率标准差很大,说明风很不稳定,这时候预测的置信度就要降低。这个信息对模型来说非常宝贵。

4.4 风速风向分解:把"方向"变成数值

风速和风向是风电预测的核心输入。风速好办,直接是个数值。但风向是角度,0度和360度其实一样,180度和0度完全相反。直接用角度数值,模型会学错。

我的做法是:把风向分解成南北分量和东西分量

def decompose_wind(wind_speed, wind_direction):
    """将风速风向分解为U和V分量"""
    # 风向:0度为正北,顺时针增加
    rad = np.deg2rad(wind_direction)
    u = wind_speed * np.sin(rad)  # 东西分量(正为东)
    v = wind_speed * np.cos(rad)  # 南北分量(正为北)
    return u, v

df['wind_u'] = df.apply(lambda row: decompose_wind(row['wind_speed'], row['wind_direction'])[0], axis=1)
df['wind_v'] = df.apply(lambda row: decompose_wind(row['wind_speed'], row['wind_direction'])[1], axis=1)

这样分解之后,模型就能理解"北风"和"南风"是相反的方向,而不是两个相差180度的数值。

补充一点:我还会额外计算风速的平方和立方。因为风功率和风速的三次方成正比,这个非线性关系,让模型自己去学不如直接给它。

4.5 本章知识体系

下面这张图总结了本章的核心内容,你可以对照着回顾一下:

时间特征工程知识体系 时间特征工程 时间周期性特征 小时编码 月份编码 季节编码 滞后特征 1h/3h滞后 6h/12h滞后 24h滞后 滑动窗口统计 均值/标准差 最大/最小值 趋势斜率 风速风向分解 U分量(东西) V分量(南北) 目标:让模型理解时间的周期性、趋势性和方向性

好了,这一章的内容就到这里。时间特征工程看起来简单,但做得好不好,直接决定模型的上限。我建议你拿到数据后,先把这些特征都做一遍,然后对比一下加与不加的效果——相信我,你会看到明显的差异。


专注资料整理