3. 时间窗口特征:滑动窗口统计、时间衰减函数、周期性特征提取

好,咱们进入第三章。时间窗口特征,这名字听着挺学术,说白了就是——看过去一段时间的数据,来推测当前这笔交易是不是有问题

我个人习惯把这类特征叫做“回头看”特征。你想想看,一个盗刷的账户,它过去5分钟的交易频率、金额均值,跟正常用户肯定不一样。这就是时间窗口要捕捉的东西。

核心思想:交易行为在时间维度上的异常,往往比单笔交易的特征更明显。

3.1 滑动窗口统计:最基础也最实用

滑动窗口,就是设定一个固定长度的时间段(比如5分钟、1小时),然后在这个窗口内做统计。我最早做风控时,第一个上线的特征就是“过去1小时交易次数”。

常见的统计量有这些:

  • 计数:窗口内交易笔数
  • 求和:窗口内交易金额总和
  • 均值:窗口内平均交易金额
  • 标准差:窗口内金额波动程度
  • 最大值/最小值:窗口内极端值

代码实现其实不复杂。我一般用Pandas的rolling函数,配合groupby按用户分组。给你看个例子:

import pandas as pd

# 假设df是交易数据,包含user_id、trans_time、amount三列
# 先按用户和时间排序
df = df.sort_values(['user_id', 'trans_time'])

# 按用户分组,对amount做5分钟滑动窗口求和
df['amt_sum_5min'] = df.groupby('user_id')['amount'] \
    .transform(lambda x: x.rolling(window='5min', min_periods=1).sum())

# 同理,计数
df['cnt_5min'] = df.groupby('user_id')['amount'] \
    .transform(lambda x: x.rolling(window='5min', min_periods=1).count())

小提示:min_periods参数很重要。如果窗口内数据太少,统计量可能不稳定。我一般设成1,至少保证有当前这笔交易。

嗯,这里要注意一点:窗口的起点。你是用当前交易往前推5分钟,还是用整点时间?我个人习惯用“当前交易时间往前推”,这样更贴合实时风控的场景。

3.2 时间衰减函数:让近期的数据更“值钱”

滑动窗口有个问题——窗口内的所有交易权重一样。但你想啊,5分钟前的那笔交易,跟10秒前的那笔,对当前的影响能一样吗?

所以就有了时间衰减。说白了就是:越近的交易,权重越大;越远的交易,权重越小

我常用的衰减函数有两种:

  • 指数衰减:权重 = exp(-λ * 时间差)
  • 线性衰减:权重 = 1 - (时间差 / 窗口长度)

指数衰减用得更多,因为它的衰减速度可以调。λ越大,衰减越快。我在项目中遇到过刷单团伙,他们会在短时间内密集交易,这时候把λ调大一点,能更灵敏地捕捉到异常。

代码实现也不难:

import numpy as np

def exponential_decay_weights(time_diffs, lambda_=0.1):
    """
    time_diffs: 当前时间与历史交易的时间差(秒)
    lambda_: 衰减系数,越大衰减越快
    """
    weights = np.exp(-lambda_ * time_diffs)
    return weights / weights.sum()  # 归一化

# 假设当前交易时间是t_now,历史交易时间是t_hist
time_diffs = (t_now - t_hist).total_seconds()
weights = exponential_decay_weights(time_diffs, lambda_=0.05)

# 加权平均金额
weighted_avg_amount = np.sum(weights * hist_amounts)

避坑指南:我曾经把λ设得太大,结果只有最近一笔交易有权重,其他全被忽略。这跟没做衰减没啥区别。建议先画个权重曲线看看,再上线。

3.3 周期性特征提取:捕捉“规律性”异常

交易行为是有周期的。比如:

  • 工作日 vs 周末,交易量不同
  • 白天 vs 深夜,交易类型不同
  • 月初 vs 月末,金额分布不同

周期性特征,就是把这些规律量化出来。我常用的方法有:

  1. 时间戳分解:提取小时、星期几、是否节假日
  2. 傅里叶变换:把时间序列转成频域,找主周期
  3. 自相关分析:看不同时间滞后下的相关性

最直接的是第一种。给你看个例子:

# 提取时间特征
df['hour'] = df['trans_time'].dt.hour
df['day_of_week'] = df['trans_time'].dt.dayofweek  # 0=周一, 6=周日
df['is_weekend'] = df['day_of_week'].isin([5, 6]).astype(int)
df['is_night'] = ((df['hour'] >= 22) | (df['hour'] <= 5)).astype(int)

# 然后可以统计:该用户在这个小时的历史平均交易金额
user_hour_avg = df.groupby(['user_id', 'hour'])['amount'].mean()

傅里叶变换稍微复杂点,但对付“每天固定时间点交易”的机器人特别有效。我记得有个案例,一个盗号团伙每天凌晨3点准时交易,用傅里叶变换一眼就看出来了。

下面这张图,展示了时间窗口特征的完整知识体系:

时间窗口特征知识体系 滑动窗口统计 时间衰减函数 周期性特征提取 常用统计量 • 计数、求和、均值 • 标准差、最大/最小值 • 窗口长度选择 • min_periods参数 衰减方式 • 指数衰减:exp(-λt) • 线性衰减:1 - t/T • λ参数调优 • 权重归一化 提取方法 • 时间戳分解 • 傅里叶变换 • 自相关分析 • 节假日特征 三者结合使用,效果最佳 应用场景:实时风控、反欺诈、异常检测

3.4 实战中的组合策略

这三种特征不是孤立的。我一般会这样组合:

场景 推荐特征组合 说明
盗刷检测 滑动窗口计数 + 指数衰减均值 短时间内高频交易,衰减让最近交易权重更大
洗钱识别 滑动窗口求和 + 周期性小时特征 大额交易集中在特定时段,周期特征能捕捉
机器人攻击 傅里叶变换 + 滑动窗口标准差 固定时间间隔交易,频域特征明显

我的经验:别一上来就搞复杂模型。先用滑动窗口+简单衰减,跑一版baseline。效果不够再上傅里叶。很多时候,简单的特征反而更稳定。

好了,时间窗口特征就聊到这儿。这三种方法,你可以在项目里灵活搭配。记住一点:特征不是越多越好,关键是能抓到异常行为的本质

专注资料整理