4. 特征工程基础:时间特征提取与金额特征构建

各位同学,今天我们进入特征工程的核心环节。说实话,特征工程在风控模型中的地位,我个人觉得比算法选择还要重要。你想想看,再牛的模型,喂进去的是垃圾特征,出来的只能是垃圾结果。我在做交易反欺诈项目时,就吃过这个亏——一开始模型AUC死活上不去,后来发现是时间特征没处理好。

好,咱们直接进入正题。今天讲两块内容:时间特征金额特征。这两类特征是交易行为异常识别中最基础、也最有效的特征。

4.1 时间特征提取:从时间戳里挖出行为模式

原始数据里通常只有一个时间戳字段,比如 2024-03-15 14:23:45。但直接把这个字段扔进模型?不行。模型看不懂时间,它只认数字。我们需要把时间拆解成有意义的特征。

4.1.1 小时特征:捕捉昼夜行为差异

为什么小时特征重要?因为正常人和机器人的行为时间分布完全不同。正常人凌晨3点还在疯狂转账?概率极低。但盗号者或脚本程序可不管白天黑夜。

提取小时特征很简单:

import pandas as pd

# 假设df['trans_time']是datetime类型
df['hour'] = df['trans_time'].dt.hour

# 我习惯再做一个"是否深夜"的二元特征
df['is_night'] = ((df['hour'] >= 23) | (df['hour'] <= 5)).astype(int)
我的经验: 小时特征不要只用0-23的数值。我建议做周期性编码,因为23点和0点其实很近,但数值上差了很多。用sin/cos变换可以解决这个问题。
# 周期性编码示例
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)

4.1.2 星期特征:区分工作日与周末

星期几这个特征,在风控里很有用。我记得有个项目,发现周末的异常交易比例比工作日高出30%。为什么?因为周末银行风控人员少,骗子就挑这时候下手。

df['weekday'] = df['trans_time'].dt.weekday  # 0=周一, 6=周日
df['is_weekend'] = (df['weekday'] >= 5).astype(int)  # 周六日标记为1
关键点: 星期特征不要只做数值编码。我建议做one-hot编码目标编码。因为星期几和异常率之间往往是非线性关系——比如周五和周六的异常模式可能完全不同。

4.1.3 节假日特征:容易被忽略的强特征

节假日特征,说白了就是判断交易日期是不是法定节假日。这个特征在电商大促、春节前后特别重要。我曾经遇到过一个案例:某个账号平时交易量很小,但一到春节就疯狂转账,后来查出来是盗号团伙在利用节日流量做掩护。

提取节假日特征需要一份节假日列表:

# 假设我们有一个节假日列表
holidays = ['2024-01-01', '2024-02-10', '2024-02-11', ...]  # 春节等

df['is_holiday'] = df['trans_time'].dt.date.isin(holidays).astype(int)

# 我还会做一个"节前节后"的特征
df['days_to_holiday'] = ...  # 距离最近节假日的天数
注意: 节假日列表需要每年更新。不同国家的节假日不同,如果是跨境交易,还要考虑交易对手所在国的节假日。我曾经因为没考虑时区差异,导致节假日特征完全失效——嗯,这个坑我踩过。

4.2 金额特征:从数字里看异常

金额特征是交易异常识别中最直观的特征。但直接使用原始金额?不够。我们需要从统计角度挖掘更深层的模式。

4.2.1 均值与标准差:刻画用户的"正常"画像

每个用户都有自己的交易习惯。有人喜欢小额高频,有人习惯大额低频。我们需要为每个用户建立"正常行为基线"。

# 按用户分组,计算历史交易的均值和标准差
user_stats = df.groupby('user_id')['amount'].agg(['mean', 'std']).reset_index()
user_stats.columns = ['user_id', 'amount_mean', 'amount_std']

# 合并回原数据
df = df.merge(user_stats, on='user_id', how='left')

这里有个细节:窗口期选择。我建议用最近30天或90天的数据来计算,而不是用全部历史。因为用户的行为会变化——比如用户升职加薪了,消费水平自然提高。

我的习惯: 计算均值时,我会剔除当前交易本身,避免数据泄露。用expandingrolling函数可以实现。
# 用rolling窗口,避免数据泄露
df['amount_mean_30d'] = df.groupby('user_id')['amount'].transform(
    lambda x: x.shift(1).rolling(30, min_periods=5).mean()
)

4.2.2 Z-score:标准化后的异常检测利器

Z-score说白了就是告诉你:当前这笔交易,偏离用户正常水平多少个标准差。公式很简单:

Z = (当前金额 - 用户均值) / 用户标准差

df['amount_zscore'] = (df['amount'] - df['amount_mean']) / df['amount_std']

Z-score的解读:

  • |Z| < 2:正常范围(约95%的交易落在这里)
  • 2 < |Z| < 3:可疑,需要关注
  • |Z| > 3:高度异常,大概率是风险交易
实战经验: 我一般会把Z-score大于3的交易直接标记为高风险。但要注意——如果用户历史交易太少(比如新用户),标准差会很小,导致Z-score虚高。这时候需要设置最小样本量,比如至少5笔交易才计算Z-score。

4.3 本章知识体系总览

下面这张图,是我梳理的时间特征与金额特征的核心逻辑。你可以把它当作一个checklist,做特征工程时对照着来。

交易行为异常识别 - 特征工程核心框架 原始交易数据 时间特征提取 金额特征构建 小时特征 星期特征 节假日特征 均值/标准差 Z-score 模型训练特征矩阵 注:时间特征与金额特征需结合使用,效果更佳

4.4 实战中的避坑指南

最后,分享几个我在项目中踩过的坑:

  1. 时间戳时区问题:跨境交易的时间戳可能是UTC时间,需要先转成用户本地时间。我曾经因为没做时区转换,导致凌晨特征全部错位。
  2. 金额特征的缺失值处理:新用户没有历史交易,均值和标准差都是空值。我一般用全局均值填充,或者单独做一个"是否新用户"的标记特征。
  3. 特征的时间窗口选择:窗口太短(比如1天),样本量不够;窗口太长(比如1年),用户行为可能已经变了。我建议用30天作为默认值,然后做超参数调优。
  4. 不要一次性生成所有特征:先做基础的时间特征和金额特征,跑一个baseline模型。然后逐步添加更复杂的特征,观察AUC提升。这样能避免过度工程化。
重要提醒: 特征工程不是越多越好。我见过有人一口气做了200多个特征,结果模型过拟合得一塌糊涂。记住:少而精才是王道。每个特征都要能解释其业务含义。

好了,时间特征和金额特征的基础就讲到这里。下一节我们会把这些特征组合起来,构建更强大的交叉特征。但今天的内容,已经足够你应付大部分交易异常识别场景了。动手试试吧!


公众号:蓝海资料掘金营,微信deep3321