数据采集与预处理:从原始数据到可用特征
大家好,我是老张。今天咱们聊聊异常交易画像构建中最基础、也最容易被忽视的一环——数据采集与预处理。
说实话,我见过太多团队把精力都花在模型调参上,结果数据质量一塌糊涂,最后模型效果惨不忍睹。嗯,这就像盖房子不打地基,直接装修——迟早要塌。
数据源类型:你面对的是什么样的数据?
做异常交易画像,我们通常要处理三类数据源。我个人习惯把它们分成「三驾马车」:
- 交易流水:这是核心数据。包括交易时间、金额、渠道、对手方信息等。说白了,就是每一笔交易发生了什么。
- 用户信息:用户注册资料、实名认证信息、历史行为标签等。用来回答「谁在交易」这个问题。
- 设备指纹:这个很多人会忽略,但我在项目中吃过亏——设备指纹能帮你识别「同一台设备在操作多个账户」这种典型异常。
关键点:这三类数据必须关联起来分析。单看交易流水,你只能看到「发生了什么」;加上用户信息,才能知道「谁干的」;再加上设备指纹,才能判断「是不是同一个人用不同马甲在搞事」。
举个例子。我曾经处理过一个案例:某个账户的交易流水看起来完全正常,金额不大、频率不高。但把设备指纹拉进来一看,发现这台设备同时登录了20多个账户,每个账户都在做类似的小额试探交易。嗯,典型的撞库攻击前兆。
数据清洗方法:脏数据怎么处理?
数据清洗,说白了就是「去伪存真」。我总结了几类常见问题:
| 问题类型 | 典型表现 | 我的处理方式 |
|---|---|---|
| 缺失值 | 交易对手方为空、设备ID缺失 | 先看缺失比例。低于5%直接删除;高于5%用众数或中位数填充 |
| 异常值 | 交易金额为负数、时间戳为未来时间 | 业务规则过滤。比如金额必须大于0,时间戳不能超过当前时间 |
| 重复数据 | 完全相同的交易记录出现多次 | 按交易流水号去重。注意:有些重复是正常的(比如重试机制),需要结合时间戳判断 |
| 格式不一致 | 手机号有的带+86,有的不带 | 统一格式化。我习惯把所有手机号转为纯数字格式 |
避坑指南:我曾经在处理一批交易数据时,发现金额字段里混入了「元」这个汉字。比如「100元」这种。当时没注意,直接转数值类型,结果全部变成NaN。嗯,从那以后我养成了一个习惯——任何字段先做类型检查,再处理。
清洗代码其实不复杂,但一定要有耐心。我一般这样写:
import pandas as pd
import numpy as np
# 加载数据
df = pd.read_csv('transaction_data.csv')
# 1. 处理缺失值
df['device_id'].fillna('unknown', inplace=True)
df['amount'].fillna(df['amount'].median(), inplace=True)
# 2. 过滤异常值
df = df[df['amount'] > 0]
df = df[df['timestamp'] <= pd.Timestamp.now()]
# 3. 去重
df.drop_duplicates(subset=['trans_id'], keep='first', inplace=True)
# 4. 格式化
df['phone'] = df['phone'].str.replace('+86', '').str.strip()
数据标准化与归一化:让不同尺度的数据能「对话」
你想想看,交易金额可能是几万块,而交易次数可能只有个位数。这两个特征直接丢进模型,金额会主导一切。所以我们需要做标准化或归一化。
我个人常用的方法有两种:
- Min-Max归一化:把数据缩放到[0,1]区间。适合数据分布比较均匀的情况。
- Z-score标准化:把数据转为均值为0、标准差为1的分布。适合有异常值的情况。
为什么会有这个区别?我举个例子。假设交易金额分布是:大部分在100-500元之间,但偶尔有几笔100万的。如果用Min-Max归一化,那100-500元的数据会被压缩到几乎看不见。而Z-score标准化能更好地保留相对关系。
我的习惯:对于异常交易检测,我倾向于先用Z-score标准化。因为异常值本身就是我们要找的目标,不能把它们「压扁」了。但如果你做的是聚类分析,Min-Max可能更合适。
代码实现也很直接:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score标准化
scaler_std = StandardScaler()
df['amount_std'] = scaler_std.fit_transform(df[['amount']])
# Min-Max归一化
scaler_mm = MinMaxScaler()
df['amount_mm'] = scaler_mm.fit_transform(df[['amount']])
知识体系总览
下面这张图是我自己画的,把数据采集与预处理的核心逻辑串起来了。你一看就明白:
你看,整个流程其实就三步:拿到数据 → 洗干净 → 统一尺度。但每一步都有坑,我踩过的坑比你们吃过的盐还多(笑)。
最后说一句:数据预处理没有银弹。每个项目的数据都不一样,你需要花时间去理解数据、观察数据。我经常说的一句话是:「花80%的时间在数据上,20%的时间在模型上」。这句话我到现在依然坚信。