数据预处理:缺失值处理、异常值检测与处理
说实话,做图谱变量衍生之前,大部分人会把精力花在特征创造上。但我个人的经验是,数据预处理没做好,后面所有工夫都可能白费。你想想看,一个带缺失值的变量衍生出10个新特征,那10个特征里全是空洞。异常值更狠,它可能直接把你的分箱边界拉到天上。
所以这一章,咱们就把缺失值和异常值这两块硬骨头啃下来。我会结合自己在信贷评分、用户画像项目中的真实踩坑经历,讲清楚什么时候该删、什么时候该补、什么时候该标记。
2.1 缺失值处理:是补还是删?
缺失值在真实数据里太常见了。用户没填生日、设备没上报操作系统版本、交易记录缺了商户类别……每个字段缺失的原因都不一样。
我个人的分类方法:
| 缺失类型 | 典型场景 | 我的建议 |
|---|---|---|
| 完全随机缺失 (MCAR) | 系统故障导致部分记录丢失 | 如果样本量充足,直接删除行;否则用均值/中位数填充 |
| 随机缺失 (MAR) | 女性用户更少填收入字段 | 用其他特征预测填充(如回归、KNN) |
| 非随机缺失 (MNAR) | 高收入人群故意不填收入 | 新增“是否缺失”标志位,再填充一个占位值(如-1) |
嗯,这里要注意:图谱变量里很多节点本身就是从原始字段衍生出来的。如果你在原始字段上用均值填充了缺失值,那么衍生出来的图特征也会继承这种“平均化”的错觉,反而削弱了区分能力。所以在图谱场景下,我倾向于对缺失值单独做标志位,而不是一味填充。
常见的缺失值处理方法
- 删除法:行删除(listwise)或列删除。适合缺失比例小于5%且MCAR的情况。记得先检查一下删除后样本分布有没有偏移。
- 统计填充:均值/中位数/众数。简单粗暴,但会降低方差,不适合用在需要保留极端值的场景(如收入、年龄)。
- 模型预测填充:用随机森林、KNN等模型预测缺失值。我在风控项目中常用KNN(k=5),效果比均值好不少。
- 多重插补 (MICE):产生多个填充版本,最后取平均或做不确定性分析。适合学术研究,工程上跑得慢。
代码实现其实不复杂。拿Python举例,pandas里直接 df.isnull().sum() 就能看到缺失分布。我个人习惯写一个函数,先区分缺失类型,再决定填充策略。
import pandas as pd
import numpy as np
def handle_missing(df, strategy='auto'):
"""
auto策略: 数值列用中位数填充,分类列用众数填充,同时生成缺失标志列
"""
missing_cols = df.columns[df.isnull().any()]
for col in missing_cols:
df[f'{col}_missing'] = df[col].isnull().astype(int)
if df[col].dtype in ['int64', 'float64']:
df[col].fillna(df[col].median(), inplace=True)
else:
df[col].fillna(df[col].mode()[0], inplace=True)
return df
2.2 异常值检测:别让一个离群点毁掉整张图谱
异常值在图谱变量衍生里尤其致命。一个用户的交易金额是正常值的100倍,如果直接用它衍生的图谱变量(如平均交易金额),整个邻域的特征都会被拉偏。
为什么会这样?因为图谱里的聚合操作(求和、平均)天然对异常值敏感。你想想看,一个强连通分量里只要有一个异常节点,它的影响力会顺着边传播到几十个邻居。
三大检测方法
| 方法 | 适用场景 | 阈值/参数 |
|---|---|---|
| Z-score 法 | 近似正态分布的数据 | |Z| > 3 视为异常 |
| IQR 箱线图法 | 偏态分布,稳健 | 下界 Q1-1.5IQR,上界 Q3+1.5IQR |
| DBSCAN 聚类法 | 高维空间或图谱嵌入 | eps 和 minPts 需调参 |
我个人在金融风控里最常用的是 IQR 法,因为它不依赖正态假设,对长尾分布很友好。不过要注意:当数据本身就有业务含义的极端值时(比如黑产账户的交易次数可能极少),IQR 会把这些正常值也误判为异常。
异常值的处理策略
- 删除:仅当你能确认这是数据录入错误或系统噪声。记得记录删除日志,便于回溯。
- 截尾(Winsorization):将超过上下界的值替换为边界值。比如对收入变量,把大于99%分位数的值设为99%分位数。
- 分箱:直接把连续值离散化,异常值自然落在最边缘的箱子里。自动分箱章节会详细讲。
- 标记:新增一个二值特征“是否异常”,保留原始值。这样模型可以自己学习异常权重。
如果后续要做图谱变量衍生,我强烈建议采用“截尾+标记”组合。先对原始字段做截尾,再衍生图谱特征;同时把是否异常的标记也作为节点特征喂给模型。这样既控制了异常的影响,又不丢失信息。
2.3 缺失与异常联合处理的流程
实际项目中,缺失值和异常值往往是共生关系。一个字段如果大量缺失,那它本身可能就是异常记录的一部分。我的预处理流水线通常是这样的:
这张图代表了我个人在项目中实际跑通的流水线。注意两个关键点:
- 缺失值处理一定要先生成标志列,再填充。这样模型能知道哪些值是“人工补的”。
- 异常值检测放在缺失值之后,因为填充后的数据分布更稳定,异常阈值不会因为缺失值而偏移。
2.4 实操中的避坑清单
我曾遇到过的一个典型问题:对时间序列图谱变量做缺失值填充,用了前向填充法(forward fill)。结果有一条长边的缺失时间窗口超过30天,填充时把30天前的值一直往后推,导致图上的时间特征完全错位。后来我改成:连续缺失超过5个时间点就标记为“异常时间窗口”,用前后平均值填充。
整理成清单给大家参考:
| 场景 | 坑 | 对策 |
|---|---|---|
| 数值型缺失率>50% | 填充会引入大量噪声 | 直接删除该列,或新建二值“是否有值”特征 |
| 类别型缺失 | 用众数填充会掩盖稀有类别 | 新增“未知”类别(字符串"unknown") |
| 极端异常值 | 截尾后改变业务含义 | 先和业务确认合理上限,用业务阈值而非统计阈值 |
| 多重共线性与异常 | 高相关特征中的异常会相互放大 | 先对特征做PCA降维,再在低维空间检测异常 |
好了,到这步你的数据已经清洗干净了,接下来就可以进入真正的图谱变量衍生环节。不过在那之前,别忘了对清洗后的数据做一次分布检查——把缺失和异常处理前后的分布对比图打印出来,能帮你快速发现问题。嗯,这个习惯我一直保留着。