2. 数据预处理与探索:数据清洗、缺失值处理、特征工程基础、数据分布可视化

大家好,我是老蓝。今天咱们聊聊数据预处理和探索。说实话,在量化风控这个领域,我见过太多人一上来就急着建模,结果模型跑出来一塌糊涂。为什么?因为数据没洗干净。我个人的习惯是,拿到数据后,至少花一半的时间在预处理上。你想想看,垃圾数据进去,再牛的算法也白搭。

核心观点: 数据预处理不是苦力活,而是决定模型上限的关键步骤。异常检测的成败,往往在数据清洗阶段就已经注定了。

2.1 数据清洗——先把脏东西筛掉

数据清洗,说白了就是把那些明显有问题的数据找出来处理掉。我在项目中遇到过最典型的例子:某个用户的年龄字段填了 200 岁,或者交易金额是负数。这些数据不处理,模型会学出什么鬼东西来?

常见的脏数据包括:

  • 重复数据:同一个用户被录入了两次,特征完全一样
  • 异常值:比如收入字段出现 999999999,明显是录入错误
  • 格式不一致:手机号有的带区号,有的不带,有的中间有空格
  • 逻辑矛盾:比如注册时间晚于当前时间,或者年龄和身份证号对不上

嗯,这里要注意:清洗不是一刀切。我曾经因为误删了一批看似异常但实际是真实的高风险交易数据,导致模型漏掉了关键信号。所以,清洗前一定要先理解业务含义。

2.2 缺失值处理——别让空值毁了你的模型

缺失值在风控数据里太常见了。用户可能没填职业信息,或者某个征信字段查不到。怎么处理?我一般分三步走:

  1. 先看缺失比例:如果某个字段缺失超过 70%,我建议直接扔掉。留着也没用,反而增加噪声。
  2. 再看缺失模式:是随机缺失,还是跟某些特征有关?比如高收入用户更不愿意填收入字段,这就是非随机缺失。
  3. 最后选填补方法:常用的有均值填补、中位数填补、或者用模型预测填补。
我的小技巧: 在风控场景里,我习惯把缺失值单独作为一个类别来处理。比如「职业」字段缺失,我就填一个「未知」类别。因为缺失本身可能就是一个风险信号——用户故意不填,往往有问题。

举个例子,假设我们有用户的收入数据,缺失了 20%。代码处理起来很简单:

import pandas as pd
import numpy as np

# 读取数据
df = pd.read_csv('user_data.csv')

# 查看缺失比例
print(df['income'].isnull().mean())  # 输出 0.2

# 用中位数填补
median_income = df['income'].median()
df['income'].fillna(median_income, inplace=True)

# 或者创建一个缺失标记
df['income_missing'] = df['income'].isnull().astype(int)

我个人更推荐第二种方式——保留缺失标记。因为缺失本身可能携带信息。

2.3 特征工程基础——从原始数据里挖金子

特征工程,说白了就是把原始数据变成模型能理解、能利用的形式。我常说一句话:特征决定了模型的上限,算法只是逼近这个上限。

在异常检测中,常用的特征工程方法包括:

  • 数值特征标准化:比如把收入、年龄等缩放到 0-1 之间,避免量纲影响
  • 类别特征编码:比如把「职业」这种文本字段转成数值,常用 one-hot 编码
  • 时间特征提取:从交易时间中提取出「是否周末」「是否深夜」等特征
  • 聚合特征:比如计算用户过去 30 天的平均交易金额、交易次数等
避坑指南: 我曾经在做一个信贷风控项目时,把用户的「申请次数」直接作为特征输入。结果模型学出来一个规律:申请次数越多,违约率越低。这明显是反常识的。后来发现,是因为那些频繁申请的用户都被拒了,留下来的都是优质用户。这就是典型的「幸存者偏差」。所以,特征工程一定要结合业务逻辑,不能只看统计相关性。

代码示例:标准化和编码

from sklearn.preprocessing import StandardScaler, OneHotEncoder

# 标准化数值特征
scaler = StandardScaler()
df['income_scaled'] = scaler.fit_transform(df[['income']])

# one-hot 编码类别特征
encoder = OneHotEncoder(sparse=False)
occupation_encoded = encoder.fit_transform(df[['occupation']])

2.4 数据分布可视化——用眼睛发现异常

数据可视化不是花架子,而是发现异常的最直观手段。我习惯在建模前先画几幅图,看看数据长什么样。

箱线图——一眼看出离群点

箱线图能展示数据的中位数、四分位数和离群点。如果某个点超出了箱线图的 whisker 范围,那它很可能就是异常值。

import matplotlib.pyplot as plt
import seaborn as sns

# 画箱线图
plt.figure(figsize=(10, 6))
sns.boxplot(x=df['income'])
plt.title('收入分布箱线图')
plt.show()

我在项目中遇到过:一个用户的交易金额是平均值的 100 倍,箱线图上一眼就看到了。这种点,要么是 VIP 大客户,要么就是异常交易。需要进一步核实。

直方图——看分布形态

直方图能告诉你数据是正态分布、偏态分布还是双峰分布。异常检测中,如果某个特征出现「长尾」,那尾巴上的点就值得关注。

# 画直方图
plt.figure(figsize=(10, 6))
sns.histplot(df['income'], bins=50, kde=True)
plt.title('收入分布直方图')
plt.show()

你想想看,如果大部分用户的收入集中在 5000-10000 之间,突然出现一个收入 100 万的用户,是不是很可疑?直方图就能帮你快速发现这种异常。

知识体系总览

下面这张图是我自己整理的,把本章的核心逻辑串起来了。你可以保存下来,做项目时对照着看。

数据预处理与探索核心流程 原始数据 数据清洗 去重 异常值 格式统一 逻辑校验 缺失值处理 删除 填补 标记缺失 特征工程 标准化 编码 时间特征 聚合特征 数据可视化 箱线图 直方图

这张图把整个流程串起来了:从原始数据开始,经过清洗、缺失值处理、特征工程,最后用可视化来验证。每一步都环环相扣,缺一不可。

我的建议: 在实际项目中,不要跳过任何一步。我曾经为了赶进度,跳过了可视化这一步,结果模型上线后才发现数据分布有问题,导致大量误报。后来我养成了一个习惯:每做一步预处理,就画一张图看看效果。这样心里才有底。

好了,这一章的内容就到这里。数据预处理和探索是异常检测的基石,打好基础,后面的建模才能事半功倍。记住:花在数据上的时间,永远不会白费。


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