3. 数据基础与特征工程:交易数据清洗、客户画像构建、特征衍生方法论

大家好,我是老蓝。今天咱们聊聊反洗钱模型里最苦最累、但也最见功力的环节——数据基础与特征工程。

说实话,很多刚入行的朋友喜欢一上来就调模型参数,觉得那才是技术活。但我做了这么多年反洗钱,可以负责任地告诉你:模型90%的效果,取决于数据质量和特征工程。你想想看,垃圾数据进去,再牛的算法也白搭。

核心观点:反洗钱建模不是算法竞赛,而是数据工程竞赛。谁把数据洗得干净、特征造得巧妙,谁就能在实战中胜出。

3.1 交易数据清洗——别让脏数据毁了你的模型

交易数据有多脏?我举个例子。有一次我接手一个银行的项目,光客户姓名这一列,就出现了全角半角混用、中间带空格、繁体简体混杂、甚至还有emoji表情的情况。嗯,你没看错,有人用表情包当名字。

清洗交易数据,我一般按以下步骤来:

  1. 缺失值处理——交易金额、交易时间这些关键字段不能空。如果空了,要么用均值/中位数填充,要么直接剔除。我个人习惯是:金额字段用中位数填充,因为反洗钱数据里异常大额交易多,均值容易被带偏。
  2. 异常值检测——比如交易金额为0、交易时间为未来时间、对手方为空等。这些明显不合逻辑的数据,直接标记或剔除。
  3. 格式统一——日期格式统一为YYYY-MM-DD HH:MM:SS,金额统一保留两位小数,币种统一为ISO标准代码。
  4. 去重处理——同一笔交易被重复记录的情况很常见。我一般用交易流水号+交易时间+交易金额三个字段联合去重。

小技巧:清洗时别急着删数据。我习惯先建一个「数据质量报告」,把每列的缺失率、异常率、重复率都统计出来。这样既能看清数据全貌,也方便跟业务方沟通。

# 一个简单的交易数据清洗示例
import pandas as pd
import numpy as np

def clean_transaction_data(df):
    # 1. 缺失值处理
    df['amount'].fillna(df['amount'].median(), inplace=True)
    df['trans_time'].fillna(method='ffill', inplace=True)
    
    # 2. 异常值过滤
    df = df[df['amount'] > 0]  # 金额必须大于0
    df = df[df['trans_time'] <= pd.Timestamp.now()]  # 时间不能是未来
    
    # 3. 格式统一
    df['trans_time'] = pd.to_datetime(df['trans_time'])
    df['amount'] = df['amount'].round(2)
    
    return df

3.2 客户画像构建——把客户「画」出来

客户画像是什么?说白了,就是把一个客户的所有信息浓缩成一组特征。我见过很多团队只用了年龄、性别、职业这几个基础字段,这远远不够。

一个完整的反洗钱客户画像,至少应该包含以下维度:

维度 特征示例 说明
基础属性 年龄、性别、职业、学历、地区 从KYC信息中提取
账户属性 开户时长、账户类型、账户余额 反映账户生命周期
交易行为 月均交易笔数、月均交易金额、交易对手数 核心行为特征
风险标签 是否涉敏地区、是否涉敏行业、历史预警次数 外部风险信息
关系网络 关联账户数、关联交易金额、网络密度 图特征,高级玩法

我在项目中遇到过一个问题:客户画像构建好了,但模型效果就是上不去。后来发现,是因为我们把「开户时长」直接当数值用了。你想想看,开户1天和开户100天的客户,行为模式能一样吗?但开户100天和开户200天的客户,差异就没那么大了。所以,我后来把开户时长做了分箱处理,效果立竿见影。

避坑指南:我曾经犯过一个错误——把客户画像里的所有特征一股脑全扔进模型。结果特征之间高度相关,模型过拟合得一塌糊涂。记住:特征不是越多越好,而是越精越好。构建完画像后,一定要做相关性分析和特征选择。

3.3 特征衍生方法论——从数据里「挖」出金子

特征衍生,说白了就是「无中生有」。原始数据里只有交易金额、交易时间这些基础字段,但我们可以通过组合、统计、变换,造出更有价值的特征。

我常用的特征衍生方法有这几类:

  • 统计类特征——比如过去7天、30天、90天的交易金额总和、均值、标准差、最大值、最小值。这些能反映客户的交易习惯和波动情况。
  • 比率类特征——比如现金交易占比、夜间交易占比、大额交易占比。这些能刻画客户的交易偏好。
  • 时序类特征——比如交易间隔的均值、交易时间的熵值、交易金额的增长率。这些能捕捉时间维度的异常。
  • 交叉类特征——比如「年龄×交易金额」、「地区×交易频次」。这些能发现不同群体间的行为差异。
# 特征衍生示例:统计类特征
def derive_features(df):
    # 按客户分组,计算过去30天的统计量
    features = df.groupby('customer_id').agg({
        'amount': ['sum', 'mean', 'std', 'max', 'min'],
        'trans_time': ['count', 'nunique'],
        'trans_type': lambda x: (x == 'cash').sum() / len(x)  # 现金交易占比
    })
    
    # 展平列名
    features.columns = ['_'.join(col).strip() for col in features.columns.values]
    features.reset_index(inplace=True)
    
    return features

为什么会这样?因为洗钱行为往往隐藏在「正常」的表象之下。一个普通客户可能每天交易3-5笔,但洗钱账户可能突然在某一天交易50笔。这种「突然变化」就是通过时序类特征捕捉到的。

我建议你从最简单的统计类特征开始,逐步增加复杂度。别一上来就搞什么深度学习特征,先把基础打牢。

核心方法论:特征衍生的本质是「降维打击」——把原始数据中的信息,通过业务理解转化为模型能识别的信号。你越懂业务,特征就造得越准。

3.4 本章知识体系总览

下面这张图,是我自己梳理的本章知识体系。你可以把它当作一个「地图」,随时回来对照。

反洗钱模型:数据基础与特征工程知识体系 交易数据清洗 • 缺失值处理 • 异常值检测 • 格式统一 • 去重处理 • 数据质量报告 客户画像构建 • 基础属性 • 账户属性 • 交易行为 • 风险标签 • 关系网络 特征衍生方法论 • 统计类特征 • 比率类特征 • 时序类特征 • 交叉类特征 • 业务理解驱动 核心原则:数据质量决定模型上限,特征工程决定模型下限 清洗 → 画像 → 衍生 → 迭代 原始交易数据 清洗后数据 客户画像 特征矩阵 → 模型训练

嗯,这张图把三个核心模块串起来了。你从原始交易数据开始,先做清洗,再构建客户画像,最后衍生特征。每一步都环环相扣,缺一不可。

我的建议:刚开始做特征工程时,别追求花哨。先把清洗和画像这两个基础打扎实。我见过太多人一上来就搞复杂特征,结果数据本身都是脏的,白费功夫。

好了,这一章的内容就到这里。数据清洗、客户画像、特征衍生,这三板斧你掌握了,反洗钱建模的基础就算打牢了。下一章我们聊聊特征选择和降维,到时候见。


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