4. 数据采集与预处理:数据源、清洗、标准化与缺失值处理
数据采集是图谱工程的「地基」。地基没打牢,后面算法再花哨也白搭。我个人做了这么多年反欺诈项目,最深的体会就是:数据源选不对、洗不干净,变量衍生出来的全是噪音。这一章咱们就把「数据怎么来、怎么洗、怎么对齐、怎么补缺」这四件事捋清楚。
4.1 三大数据源:交易日志、社交关系、通话记录
团伙识别绕不开三个典型数据源。它们各有脾气,处理方式也不同。
- 交易日志:银行核心系统、支付平台产生的转账、消费、提现等流水。字段通常包含交易时间、金额、对手方账号、设备指纹、IP 等。量大,时效性强。
- 社交关系:微信、QQ 的聊天记录、群关系、好友列表、转发链。结构半开放,隐私脱敏要求高,字段偏文本和关系对。
- 通话记录:通信网元的 CDR(话单),包含主叫、被叫、时长、基站位置。密度高,但原始信令数据很大,一个中等城市一天就有十几亿条。
4.2 数据源特点对比
| 维度 | 交易日志 | 社交关系 | 通话记录 |
|---|---|---|---|
| 数据量级 | 百万~亿级/天 | 万~千万级/天 | 千万~百亿级/天 |
| 核心字段 | 时间、金额、账号、IP | 好友关系、群 ID、消息类型 | 主叫、被叫、时长、基站 |
| 敏感等级 | 高(资金安全) | 极高(隐私法) | 高(通信隐私) |
| 常见噪声 | 重复流水、抹零交易 | 僵尸号、系统水军 | 接通失败、漫游漂移 |
你看,这三个源的数据质量参差不齐。交易日志相对规整,但容易出现重复报文——我曾经遇到过一个项目,交易系统因为双写,同一条转账记录在 Kafka 里出现了三次,导致后续聚类图的边多了一倍。所以清洗的第一步就是去重。
4.3 数据清洗:去噪、去重、字段格式化
清洗不是「一刀切干净」,而是保留有价值的信息、剔除肯定有问题的记录。我通常会按这个流程来做:
- 字段完整性检查:必填字段(如交易ID、对方账号)为空则直接丢掉或少全量回溯。
- 格式校验:手机号位数、IP 合法性、金额范围。例如通话记录里时长字段如果出现负值,一定是设备异常,直接过滤。
- 语义去重:精确去重很容易,难的是「近似重复」。比如两笔交易时间差 1 秒、金额差 0.01 元、账号不同——多半是系统拆单,你得保留一份主单。
- 低价值清洗:僵尸号、测试账户、系统内部流转标记,这些对团伙分析无益,提前剔除。
# 伪代码示例 - 交易日志去重与格式校验
def clean_transaction(df):
# 去重:基于交易ID + 时间窗口
df = df.drop_duplicates(subset=['tx_id', 'tx_time'])
# 格式校验:金额必须 > 0 且 < 1e8
df = df[(df['amount'] > 0) & (df['amount'] < 1e8)]
# 剔除测试账号(已知名单)
df = df[~df['account'].isin(TEST_ACCOUNTS)]
return df
4.4 标准化:把「方言」翻译成「普通话」
三个数据源的字段名、时间格式、单位可能完全不同。标准化就是把它们统一到同一套字典里。我见过太多团队花 80% 时间在参数对齐上。
标准化的核心动作:
- 时间对齐:所有时间戳转成 UTC+8,统一粒度到秒。交易日志用毫秒,通话记录用秒,那就截断取整。
- 实体 ID 映射:手机号、设备指纹、身份证号,用统一的 hash 或脱敏标识符。例如手机号取前三位+后四位,但不能丢失关联性。
- 金额单位统一:交易日志可能用「分」,社交关系里的红包金额用「元」,统一为「元」保留两位小数。
- 类别编码:交易类型、通话状态等离散值,映射为统一编码表。
# 标准化示例 - 时间 & 金额
def standardize_transaction(df):
df['tx_time'] = pd.to_datetime(df['tx_time'], unit='ms').tz_localize('UTC').tz_convert('Asia/Shanghai')
df['amount'] = df['amount'] / 100.0 # 分转元
df['phone_hash'] = df['phone'].apply(lambda x: hashlib.md5(x.encode()).hexdigest()[:16])
return df
4.5 缺失值处理:不补、补均值、还是用图传播?
缺失值是团伙识别里的常客。比如通话记录里基站 ID 缺失,交易日志里 IP 缺失,社交关系里性别字段缺失。处理方法没有银弹,我按场景分级:
| 缺失程度 | 举例 | 我推荐的方法 |
|---|---|---|
| < 5% | 少量 IP 字段缺 | 直接删除该记录,或填充一个占位符(如 '0.0.0.0') |
| 5%~30% | 部分基站的 GPS 缺 | 使用同基站其他记录的众数填充 |
| > 30% | 社交关系里的「职业」字段 | 保留缺失标记,作为后续图特征的「未知」维度 |
核心原则:在团伙识别的图变量衍生中,缺失值本身可能就是一个强特征。例如某个账号的「设备型号」长期缺失,很可能是因为使用了模拟器或刷机工具——这在欺诈团伙中常见。
我不建议对图数据做针对字段的均值填充。因为图上的节点往往被多个边连接,一个缺失的 node attribute 可以用邻居的聚合值来估计,这就是图传播填充(Graph Propagation Imputation)。虽然复杂,但对团伙识别的精度提升明显。
4.6 本章小结:一张图看明白预处理流程
最后我把整个数据采集与预处理的步骤画成了一幅流程图,方便你在项目中直接参考。注意,这张图只画了最核心的环节——实际落地时你还要考虑数据分区、压缩存储、增量更新等工程细节。
嗯,数据预处理这一步虽然看起来「底层」,但它决定了图谱能否真实反映团伙结构。我每次带新人时都会让他先把这章的内容在真实数据上跑一遍——跑通了,后面的变量衍生自然顺。