4. 原始拒绝原因清洗:缺失值处理、异常值检测、文本标准化、重复记录去重

原始拒绝原因数据通常来自风控系统的日志、客服工单、审批备注等异构来源,数据质量参差不齐。在进入变量衍生与分箱之前,必须对原始字段进行系统性清洗。本章聚焦四个核心清洗步骤:缺失值处理、异常值检测、文本标准化、重复记录去重。

核心原则:清洗策略必须与业务逻辑对齐。拒绝原因中的“缺失”有时本身就是一种信号(如系统未捕获到规则命中),不能简单填充或删除。

4.1 缺失值处理

拒绝原因字段的缺失模式可分为三类:

缺失类型 业务含义 推荐处理方式
系统未记录 风控引擎未输出拒绝原因码 标记为“UNKNOWN”或“SYS_NULL”,保留为独立类别
人工审批未填写 审批人跳过备注 填充为“MANUAL_BLANK”,或根据审批结论反向推断
数据迁移丢失 历史数据字段为空 按时间窗口分组众数填充,或标记为“MIGRATE_NULL”
经验法则:拒绝原因的缺失率超过 30% 时,建议先排查上游数据链路,而非直接填充。缺失本身可能暗示系统故障或规则配置遗漏。
# Python 示例:拒绝原因缺失值标记与分组填充
import pandas as pd

df['reject_reason_clean'] = df['reject_reason_raw'].fillna('UNKNOWN')

# 按日期分组,用当日高频拒绝原因填充缺失
df['reject_reason_clean'] = df.groupby('apply_date')['reject_reason_clean']\
    .transform(lambda x: x.fillna(x.mode()[0] if not x.mode().empty else 'UNKNOWN'))
缺失值处理决策流程 原始拒绝原因字段 是否缺失? 是 标记缺失类型 否 保留原值 输出清洗后字段

4.2 异常值检测

拒绝原因中的异常值通常表现为:不合规的编码、超出枚举范围的字符串、明显错误的业务逻辑(如“通过”出现在拒绝原因中)。

  • 编码异常:拒绝原因码不在预定义枚举表中,如出现“REJECT_9999”或空字符串。
  • 语义异常:文本内容与拒绝场景矛盾,如“审批通过”被填入拒绝原因字段。
  • 格式异常:包含不可见字符、全半角混用、多余空格或换行符。
注意:不要将低频但合法的拒绝原因误判为异常。例如“欺诈团伙关联”虽然出现频率极低,但属于有效拒绝原因,应保留而非剔除。
# 异常值检测示例:基于枚举表与正则规则
valid_reasons = ['FRAUD', 'OVER_LIMIT', 'RISK_SCORE_HIGH', 'BLACKLIST']
df['is_anomaly'] = ~df['reject_reason_clean'].isin(valid_reasons)

# 检测包含非法字符的记录
import re
df['has_special_char'] = df['reject_reason_clean'].apply(
    lambda x: bool(re.search(r'[^\w\s\-_]', str(x)))
)

4.3 文本标准化

同一拒绝原因在不同系统或不同时期可能有多种写法,标准化是变量衍生前的必要步骤。

标准化类型 原始示例 标准化后
大小写统一 "Fraud" / "fraud" / "FRAUD" FRAUD
缩写展开 "ovr_lim" / "overlimit" OVER_LIMIT
同义词映射 "黑名单" / "blacklist" / "BL" BLACKLIST
空格与标点清理 " 风险_评分_高 " / "风险评分高" RISK_SCORE_HIGH
# 文本标准化映射表示例
standard_map = {
    'fraud': 'FRAUD', '欺诈': 'FRAUD',
    'overlimit': 'OVER_LIMIT', 'over_limit': 'OVER_LIMIT', '超限': 'OVER_LIMIT',
    'blacklist': 'BLACKLIST', '黑名单': 'BLACKLIST', 'bl': 'BLACKLIST',
    'risk_score_high': 'RISK_SCORE_HIGH', '风险评分高': 'RISK_SCORE_HIGH'
}

df['reason_std'] = df['reject_reason_clean'].str.strip().str.lower()\
    .map(standard_map).fillna(df['reject_reason_clean'])
文本标准化流水线 原始文本 大小写统一 缩写展开 同义词映射 空格/标点清理 标准化拒绝原因 映射表需持续维护 新出现的拒绝原因 及时加入映射规则

4.4 重复记录去重

拒绝原因数据中重复记录可能来自:同一申请单被多次记录、数据采集管道重试、多系统重复上报。去重策略需区分完全重复与业务语义重复。

  • 完全重复:所有字段(申请ID、拒绝原因、时间戳)完全一致,直接保留一条。
  • 语义重复:同一申请ID出现多条不同但相似的拒绝原因(如“FRAUD”和“欺诈风险”),需合并或保留优先级最高的原因。
  • 时间窗口去重:同一申请ID在短时间窗口(如1秒内)出现多条记录,仅保留第一条。
# 完全重复去重
df_dedup = df.drop_duplicates(subset=['apply_id', 'reason_std', 'record_time'])

# 语义重复去重:同一申请ID保留最早记录
df_dedup = df.sort_values('record_time').drop_duplicates(subset=['apply_id'], keep='first')

# 时间窗口去重:1秒内同一申请ID只保留一条
df['time_round'] = df['record_time'].dt.floor('1S')
df_dedup = df.drop_duplicates(subset=['apply_id', 'time_round'])
去重后的质量检查:对比去重前后的记录数,计算重复率。若重复率超过 10%,需排查上游数据管道是否存在重复推送问题。重复率 = (原始记录数 - 去重后记录数) / 原始记录数 × 100%。

4.5 清洗效果评估

完成上述四个步骤后,建议输出一份清洗报告,包含以下指标:

评估维度 指标 合格标准
缺失率 清洗后缺失值占比 < 5%
异常率 检测出的异常记录占比 < 2%
标准化覆盖率 映射表成功匹配的比例 > 95%
重复率 去重后减少的记录占比 < 10% 或业务可接受
最终产出:清洗后的拒绝原因字段应满足:无缺失、无异常、格式统一、无重复。该字段将作为后续变量衍生(如拒绝原因编码、拒绝原因组合、拒绝原因序列特征)的可靠输入。