第三章:数据探索——用Pandas搞定数据清洗与异常筛查
数据探索这一步,说白了就是给原始数据“洗澡”。
我见过太多同学,模型调得飞起,结果一查数据全是脏的。嗯,那感觉就像你精心做了一桌菜,结果发现米是馊的。所以今天咱们就聊聊,怎么用Pandas把数据洗干净、补全、筛掉明显有问题的样本。
3.1 数据加载与初步预览
拿到交易数据,我习惯先看一眼全貌。不是直接建模,而是先摸摸底。
import pandas as pd
import numpy as np
# 加载交易数据
df = pd.read_csv('transaction_data.csv', parse_dates=['trans_time'])
print(df.shape) # 看看有多少行多少列
print(df.info()) # 数据类型、非空计数一目了然
print(df.describe()) # 数值列的统计摘要
你想想看,info() 能告诉你哪些列有缺失,describe() 能让你一眼看出金额有没有离谱的极值。我个人习惯先跑这两步,心里就有数了。
3.2 数据清洗——把脏数据揪出来
数据清洗,核心就三件事:去重、格式统一、不合理值处理。
3.2.1 重复交易检测
交易数据里重复很常见。可能是系统重试,也可能是恶意刷单。
# 检查完全重复的行
dup_rows = df.duplicated().sum()
print(f'完全重复行数: {dup_rows}')
# 更精细的:按关键字段判断重复
dup_key = df.duplicated(subset=['user_id', 'amount', 'trans_time'], keep=False)
df[dup_key].head()
我在项目中遇到过,有个支付渠道因为回调机制,同一笔交易录入了三次。如果不处理,模型会以为这个用户交易频率很高,误判成异常。所以去重这一步,千万别省。
3.2.2 数据类型修正
有时候金额列读进来是字符串,时间列是object。不改类型,后面没法算。
# 金额转浮点,错误值变NaN
df['amount'] = pd.to_numeric(df['amount'], errors='coerce')
# 时间列统一格式
df['trans_time'] = pd.to_datetime(df['trans_time'], errors='coerce')
嗯,这里要注意:errors='coerce' 会把无法转换的值变成NaN。后面我们再处理这些缺失值。
3.3 缺失值处理——补还是不补?
缺失值处理没有银弹。我的原则是:先看缺失比例,再决定策略。
| 缺失比例 | 推荐策略 | 适用场景 |
|---|---|---|
| < 5% | 直接删除或填充均值/中位数 | 缺失量小,影响不大 |
| 5% - 30% | 用中位数/众数填充,或用模型预测 | 特征重要,不能直接删 |
| > 30% | 考虑删除该特征,或单独编码“缺失” | 缺失太多,填充反而引入噪声 |
# 查看每列缺失比例
missing_ratio = df.isnull().mean().sort_values(ascending=False)
print(missing_ratio)
# 金额列缺失较少,用中位数填充
df['amount'].fillna(df['amount'].median(), inplace=True)
# 商户类别缺失较多,单独标记
df['merchant_type'].fillna('UNKNOWN', inplace=True)
3.4 异常值初步筛查——把“显眼包”找出来
异常值筛查,我把它分成两步:单变量筛查和多变量筛查。今天先讲单变量,简单粗暴但有效。
3.4.1 基于统计分布的筛查
最常用的就是IQR(四分位距)法。说白了,就是找出那些“离群太远”的点。
def detect_outliers_iqr(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
return outliers
# 检测交易金额的异常值
amount_outliers = detect_outliers_iqr(df, 'amount')
print(f'金额异常交易数: {len(amount_outliers)}')
你想想看,正常消费金额大多集中在几十到几千块。突然冒出一笔100万的交易,要么是土豪,要么就是异常。IQR法能快速帮你圈出这些候选。
3.4.2 基于业务规则的筛查
光靠统计不够,还得结合业务。比如凌晨3点的交易、短时间内多次交易,这些在统计上可能不“异常”,但在风控场景里很可疑。
# 凌晨交易(0点-5点)
df['hour'] = df['trans_time'].dt.hour
night_trans = df[df['hour'].between(0, 5)]
print(f'凌晨交易数: {len(night_trans)}')
# 短时间内多次交易(同一用户,5分钟内超过3笔)
df_sorted = df.sort_values(['user_id', 'trans_time'])
df_sorted['time_diff'] = df_sorted.groupby('user_id')['trans_time'].diff().dt.total_seconds()
rapid_trans = df_sorted[df_sorted['time_diff'] < 300] # 5分钟内
print(f'快速连续交易数: {len(rapid_trans)}')
3.5 本章知识体系总览
下面这张图,是我自己梳理的数据探索流程。你可以把它当作操作清单,每一步做完打个勾。
这张图把今天讲的内容串起来了。从加载数据开始,到清洗、补缺失、筛异常,每一步都有对应的Pandas代码。你照着这个流程走一遍,数据基本就干净了。
好了,数据探索这块就聊到这儿。记住一句话:脏数据进,脏模型出。花时间把数据洗干净,后面建模会省心很多。