时间序列特征:滑动窗口统计、滞后特征、差分特征

讲图谱变量衍生,避不开时间序列特征。为啥?因为很多业务场景里的实体关系是随时间演变的——用户在平台上的行为序列、传感器读数、交易流水,说白了都是时序数据。如果只拿当前时刻的截面值,等于扔掉了一大半信息。

我个人习惯把时序特征分成三大类:滑动窗口统计、滞后特征、差分特征。这三类各有各的用处,也各有各的坑。今天我们就一个一个掰开聊。

滑动窗口统计:让历史流动起来

什么是滑动窗口?就是给每个时间点开一个固定长度的“历史小窗”,然后在这个窗口里算统计量。你想想看,用户过去7天的平均登录次数、过去30天的最大消费金额——这些特征天然就有业务解释力。

SQL里实现滑动窗口,核心就是窗口函数的ROWS BETWEEN子句:

SELECT
  user_id,
  event_time,
  AVG(amount) OVER (
    PARTITION BY user_id
    ORDER BY event_time
    ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
  ) as avg_7day
FROM transactions;

这里我常用的是ROWS(基于行数),也可以换RANGE(基于时间范围),但RANGE在SQLite里支持不完整,我一般在生产中都用ROWS配合排序字段做等间隔采样。下边这张表列出最常见的滑动窗口统计量:

统计量SQL函数典型窗口大小业务含义
均值AVG()7天 / 30天近期平均活跃度
标准差STDDEV()7天行为波动性
最大值MAX()30天历史峰值
最小值MIN()30天历史低谷
计数COUNT()1天 / 7天频次特征
滑动比率SUM(x)/SUM(y)7天转化率、点击率
⚠ 边界坑:我曾经在新项目里直接用默认窗口计算前几行的均值,结果前7天的窗口行数不足7行,均值被低估了。后来我加了个 COUNT(*) 做分母校验,小于最小行数就置NULL。嗯,这个坑很隐蔽,但一遇长尾数据必炸。

滞后特征:拉一条时间线

滞后特征(Lag Feature)就是把某时刻的值往前挪几个时间点。说白了,就是拿昨天、前天、上一周的值当今天的特征。这在检测序列依赖性时特别有用——如果今天的操作和昨天的高度相关,模型必须能感知到。

SQL里的LAG()函数是标配:

SELECT
  user_id,
  event_time,
  amount,
  LAG(amount, 1) OVER (
    PARTITION BY user_id ORDER BY event_time
  ) as amount_lag1,
  LAG(amount, 7) OVER (
    PARTITION BY user_id ORDER BY event_time
  ) as amount_lag7
FROM transactions;

我个人习惯只要业务允许,至少生成三阶滞后(lag1, lag3, lag7)。为什么?因为lag1捕捉最近依赖,lag3捕捉周中周期,lag7捕捉周同比。但注意,阶数一多,特征之间容易高度共线性。我记得有一次在风控模型里同时用了lag1~lag5,结果五阶滞后完美共线,模型直接跑了1小时没收敛。后来我只保留lag1, lag3, lag7。

💡 小技巧:如果时间序列不均匀,先做时间分桶(比如按天聚合),再做滞后。否则同一个lag值会对应不同的实时间隔,特征语义就乱了。

差分特征:把趋势变成白噪声

差分特征就是今天的值减去昨天的值(一阶差分)。很多时候原始值非平稳,但差分后的序列是平稳的——模型学起来更稳定,尤其适合线性模型和树模型里的线性部分。

SQL里实现差分很简单,用LAG做减法:

SELECT
  user_id,
  event_time,
  amount,
  amount - LAG(amount, 1) OVER (
    PARTITION BY user_id ORDER BY event_time
  ) as diff_1
FROM transactions;

差分不只一阶。如果数据有周季节性,可以做7步差分:amount - LAG(amount, 7)。我通常把一阶差分和季节差分都生成,然后让模型自己决定用哪个。

核心原则:差分只消除趋势,不消除信息。如果原始值本身已经平稳,加差分反而是噪音。建议先做ADF检验(显著性检验),平稳就不做差分。

三种特征如何协同?

实际项目中我通常这样搭配:滑动窗口统计捕捉短期聚集,滞后特征捕捉精确时间依赖,差分特征为模型提供更干净的变化信号。三者组合后特征量会翻好几倍,但同时也带来冗余。我建议先用树模型跑一轮特征重要性,把没用的砍掉。

下边这张SVG图概括了从原始时序到特征矩阵的完整衍生流程:

时间序列特征衍生流程 原始时序数据 滑动窗口统计 滞后特征(LAG) 差分特征 特征合并(JOIN) 宽表特征矩阵 说明:原始时序数据分别经过三种衍生方式,最终以时间+实体键为连接点合并为宽表特征矩阵。
💡 组合策略:对于高频数据(每分钟),我通常先做滑动窗口聚合(如每小时均值),再做滞后和差分;对于低频数据(每天),直接上滞后和差分。千万别对高频原始值直接做多阶滞后,那会生成海量稀疏特征。

嗯,这里还要提醒一点:所有时序特征衍生都依赖数据按时间排序。SQL里的ORDER BY一定要加准确,不然特征值串了位,模型学到的全是噪声。我习惯在生成特征前先跑一次ROW_NUMBER()校验顺序,确保没有重复时间戳。

最后,我个人不推荐在一个SQL里把所有时序特征一股脑全生成。拆成多个CTE(公共表表达式),每个CTE负责一类特征,最后再用JOIN拼合。这样逻辑清晰,也方便调试。毕竟,时序衍生一旦出错,排查起来比普通特征麻烦得多。