3. 时间特征工程

任务目标

本案例以某电商平台日度订单量数据为输入,完成两类时间特征工程:(1) 从时间戳中提取星期、月份、是否周末、是否节假日等日历特征;(2) 基于历史序列构造滞后特征与滚动统计特征(滚动均值、滚动标准差),最终输出可直接用于模型训练的增强特征表。

环境与依赖

Python 3.8+,仅需标准库 datetime、statistics 及 csv(若需读写文件)。无需第三方库,便于独立运行验证。

关键完整代码

import csv
from datetime import datetime, timedelta
from statistics import mean, stdev

# 节假日集合(示例:2023年部分中国法定节假日)
HOLIDAYS = {
    datetime(2023, 1, 1), datetime(2023, 1, 2), datetime(2023, 1, 21),
    datetime(2023, 1, 22), datetime(2023, 1, 23), datetime(2023, 1, 24),
    datetime(2023, 1, 25), datetime(2023, 1, 26), datetime(2023, 1, 27),
    datetime(2023, 4, 5), datetime(2023, 5, 1), datetime(2023, 5, 2),
    datetime(2023, 5, 3), datetime(2023, 6, 22), datetime(2023, 6, 23),
    datetime(2023, 6, 24), datetime(2023, 9, 29), datetime(2023, 10, 1),
    datetime(2023, 10, 2), datetime(2023, 10, 3), datetime(2023, 10, 4),
    datetime(2023, 10, 5), datetime(2023, 10, 6)
}

def extract_calendar_features(dt):
    """从单个时间戳提取日历特征"""
    return {
        'year': dt.year,
        'month': dt.month,
        'day': dt.day,
        'day_of_week': dt.weekday(),  # 0=周一, 6=周日
        'is_weekend': 1 if dt.weekday() >= 5 else 0,
        'is_holiday': 1 if dt.date() in {h.date() for h in HOLIDAYS} else 0
    }

def build_features(dates, values, lag=3, window=7):
    """
    为时间序列构造完整特征矩阵
    dates: list[datetime], values: list[float], 按时间升序排列
    lag: 滞后阶数, window: 滚动窗口大小
    返回: (特征字典列表, 目标值列表)
    """
    if len(dates) != len(values):
        raise ValueError("dates 和 values 长度不一致")
    if len(dates) < lag + window:
        raise ValueError(f"数据量不足,至少需要 {lag + window} 条")

    features_list = []
    targets = []
    # 从索引 lag+window-1 开始,保证每个样本都有完整的滞后与滚动窗口
    for i in range(lag + window - 1, len(dates)):
        feat = extract_calendar_features(dates[i])
        # 滞后特征
        for l in range(1, lag + 1):
            feat[f'lag_{l}'] = values[i - l]
        # 滚动统计特征(窗口内不含当前值)
        window_vals = values[i - window:i]
        feat['rolling_mean'] = round(mean(window_vals), 4)
        feat['rolling_std'] = round(stdev(window_vals), 4) if len(window_vals) > 1 else 0.0
        features_list.append(feat)
        targets.append(values[i])
    return features_list, targets

# ========== 最小可运行示例 ==========
if __name__ == '__main__':
    # 构造2023年1月1日至1月31日的模拟订单数据(仅用于演示)
    start = datetime(2023, 1, 1)
    dates = [start + timedelta(days=i) for i in range(31)]
    values = [100 + (i % 7) * 10 + (5 if dates[i].weekday() >= 5 else 0) for i in range(31)]

    feats, targets = build_features(dates, values, lag=3, window=7)
    print(f"共生成 {len(feats)} 个样本")
    print("首个样本特征:", feats[0])
    print("首个样本目标:", targets[0])

    # 验证:检查特征完整性
    required_keys = {'year', 'month', 'day', 'day_of_week', 'is_weekend',
                     'is_holiday', 'lag_1', 'lag_2', 'lag_3', 'rolling_mean', 'rolling_std'}
    assert required_keys.issubset(feats[0].keys()), "特征缺失"
    print("特征完整性验证通过")

运行与验证方式

  1. 将上述代码保存为 time_features.py,在命令行执行 python time_features.py。
  2. 程序输出样本数量、首个样本的完整特征字典及目标值,并执行特征完整性断言。
  3. 通过条件:程序无异常退出,输出“特征完整性验证通过”,且首个样本特征中包含全部11个键(4个日期分量、2个布尔标志、3个滞后值、2个滚动统计量)。

关键说明

  • 节假日集合仅包含2023年示例日期,实际使用时应替换为目标预测时段对应的节假日表(如使用 chinese_calendar 库或自行维护)。
  • 滚动统计使用不含当前值的前window个历史值计算,避免信息泄漏;滞后阶数 lag 与窗口 window 可按需调整。
  • 代码从索引 lag+window-1 开始生成样本,确保每个样本都有完整的滞后窗口和滚动窗口,这是时序特征工程中防止未来信息泄漏的标准做法。