第二章 审计数据预处理实战:数据清洗、集成与变换
各位同行,咱们直接进入正题。数据预处理这事儿,说白了就是给审计数据「洗澡」。我做了十几年金融审计,见过太多「脏数据」把模型带沟里的案例。你想想看,如果原始数据本身就有问题,后面再花哨的分析都是白搭。
核心观点:审计数据预处理不是技术活,是「侦探活」。你要从数据里找出那些隐藏的猫腻。
2.1 缺失值处理:别让「空白」骗了你
缺失值在审计数据里太常见了。我遇到过一家银行,某笔贷款的交易金额字段全是空值。一开始以为是系统bug,后来一查——好家伙,是有人故意删掉的。
处理缺失值,我个人的习惯是三步走:
- 先判断缺失类型:是随机缺失、完全随机缺失,还是非随机缺失?
- 再评估影响:缺失比例超过30%的字段,我建议直接放弃。
- 最后选方法:均值填充、中位数填充、还是用模型预测?
我的经验:审计数据里,缺失值往往不是「偶然」。我曾经处理过一个案例,某笔交易的对手方名称缺失,结果发现是关联交易被刻意隐藏。所以,遇到缺失值,先别急着填充,查查原因再说。
Python里处理缺失值,我常用pandas:
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('audit_data.csv')
# 查看缺失情况
print(df.isnull().sum())
# 填充缺失值(谨慎使用!)
df['amount'].fillna(df['amount'].median(), inplace=True)
# 或者直接删除缺失严重的行
df.dropna(subset=['transaction_id'], inplace=True)
避坑指南:我曾经用均值填充了一组缺失的利率数据,结果导致后续的异常检测模型完全失效。因为那组缺失值其实是高利率贷款,均值填充把异常信号给抹平了。
2.2 异常值检测:审计的「雷达」
异常值检测,是审计数据挖掘的核心。说白了,我们就是要找那些「不对劲」的数据点。
常用的方法有:
- Z-score方法:适合正态分布的数据
- IQR方法:适合偏态分布,审计数据里更常用
- 孤立森林:适合高维数据,我最近用得比较多
举个例子,我在审计某券商的自营交易时,发现一笔债券交易的收益率比市场平均高出5倍。用IQR方法一算,果然是异常值。后来查实,是交易员为了完成业绩指标,虚报了交易价格。
# IQR方法检测异常值
Q1 = df['amount'].quantile(0.25)
Q3 = df['amount'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['amount'] < lower_bound) | (df['amount'] > upper_bound)]
print(f"发现 {len(outliers)} 个异常值")
注意:审计场景下,异常值不一定是错误。有时候,异常值恰恰是问题的线索。我建议把异常值单独拎出来,做人工复核。
2.3 数据集成:把碎片拼成地图
金融审计的数据来源五花八门:核心系统、风控系统、外部征信、甚至Excel手工台账。数据集成,就是把这些碎片拼成一张完整的地图。
我遇到过最头疼的情况:同一家企业的名称,在三个系统里分别是「XX有限公司」「XX有限责任公司」「XX公司」。你想想看,如果不做清洗,关联交易分析根本没法做。
数据集成的主要步骤:
- 实体对齐:用模糊匹配或规则引擎,把同一实体合并
- 字段映射:不同系统的字段名可能不同,需要建立映射关系
- 冲突解决:同一字段在不同系统里值不一样,以哪个为准?
# 简单的模糊匹配示例
from fuzzywuzzy import fuzz
def match_company(name, reference_list):
for ref in reference_list:
score = fuzz.ratio(name, ref)
if score > 85: # 相似度阈值
return ref
return None
我的习惯:做数据集成时,我会保留一个「数据血缘」字段,记录每条数据来自哪个源系统。这样出了问题,能快速溯源。
2.4 数据变换:让数据「说话」
原始数据往往不能直接用于分析。数据变换,就是给数据「化妆」,让它更适合模型。
常用的变换包括:
- 标准化:把不同量纲的数据拉到同一尺度
- 离散化:把连续变量变成分类变量,比如把年龄分成「青年、中年、老年」
- 特征构造:从原始字段衍生出新特征,比如「交易金额/客户资产」这个比率
我记得有一次做信贷审计,原始数据里只有「贷款金额」和「客户收入」两个字段。我构造了一个「月供收入比」特征,结果这个特征直接成了识别高风险贷款的关键指标。
from sklearn.preprocessing import StandardScaler
# 标准化
scaler = StandardScaler()
df[['amount', 'rate']] = scaler.fit_transform(df[['amount', 'rate']])
# 特征构造
df['debt_to_income'] = df['monthly_payment'] / df['monthly_income']
2.5 数据预处理流水线:自动化你的工作流
审计数据预处理,最怕的就是每次都要手动重复。我建议用pipeline把流程串起来,一次写好,反复使用。
下面是我常用的一个预处理流水线框架:
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
# 定义流水线
preprocessing_pipeline = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# 应用流水线
df_clean = preprocessing_pipeline.fit_transform(df)
核心思路:把数据预处理当成一个「黑盒」,输入原始数据,输出干净数据。中间的逻辑封装好,以后换项目也能复用。
嗯,这里要注意一点:流水线虽然方便,但审计场景下,我建议保留每一步的中间结果。万一出了问题,能回溯到具体是哪一步出了问题。
这张图展示了我个人常用的数据预处理流水线。你看,从原始数据到干净数据,中间要经过缺失值处理、异常值检测、数据集成、数据变换四个环节。最后用Pipeline封装起来,形成自动化流程。
最后提醒一句:数据预处理不是一劳永逸的。审计数据的特点就是「变」,今天的数据和明天的可能就不一样。我建议定期检查预处理逻辑,确保它仍然适用。