4. 数据质量初探:缺失值统计、异常值检测、重复记录处理、数据分布概览

在信用评分建模中,数据质量直接决定模型的上限。原始数据通常包含缺失值、异常值、重复记录以及分布偏移等问题。本章将系统性地介绍如何利用 Spark 进行数据质量的初步探查,为后续的特征工程和建模奠定可靠基础。

4.1 缺失值统计

缺失值是信用评分数据中最常见的问题之一。缺失可能源于数据采集失败、用户未填写、系统错误等。在 Spark 中,我们通常使用 DataFrame 的 summary() 或自定义聚合函数来统计每列的缺失比例。

关键指标: 缺失率 = 缺失记录数 / 总记录数。通常缺失率 > 50% 的字段会被直接剔除;缺失率在 5%-50% 之间的字段需要填充或编码;缺失率 < 5% 的字段可直接删除对应行或使用均值/中位数填充。

4.1.1 缺失值统计实现

from pyspark.sql import functions as F

# 统计每列缺失数量与缺失率
def missing_stats(df):
    total_rows = df.count()
    stats = []
    for col_name in df.columns:
        missing_count = df.filter(F.col(col_name).isNull() | F.isnan(col_name)).count()
        missing_rate = round(missing_count / total_rows, 4)
        stats.append((col_name, missing_count, missing_rate))
    return spark.createDataFrame(stats, schema=["column", "missing_count", "missing_rate"])

missing_df = missing_stats(df)
missing_df.filter("missing_rate > 0").orderBy("missing_rate", ascending=False).show(10)
字段名 缺失数量 缺失率 处理建议
income 12,340 0.1234 中位数填充或模型预测填充
employment_length 8,567 0.0857 众数填充或单独编码为"未知"
credit_score 1,234 0.0123 删除对应行或均值填充
zip_code 56,789 0.5679 缺失率过高,建议剔除该字段

4.2 异常值检测

异常值会严重干扰信用评分模型的稳定性。常见的异常值检测方法包括:Z-Score 法、IQR 四分位距法、以及基于业务规则的阈值法。在 Spark 中,我们通常使用窗口函数和聚合函数进行高效检测。

4.2.1 IQR 异常值检测

# 计算每列的 Q1、Q3 和 IQR
quantiles = df.approxQuantile("loan_amount", [0.25, 0.75], 0.01)
Q1, Q3 = quantiles[0], quantiles[1]
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 标记异常值
df_with_outlier_flag = df.withColumn(
    "is_outlier",
    (F.col("loan_amount") < lower_bound) | (F.col("loan_amount") > upper_bound)
)

outlier_count = df_with_outlier_flag.filter("is_outlier = true").count()
print(f"loan_amount 字段异常值数量: {outlier_count}")
信用评分场景提示: 对于收入、负债率等字段,建议结合业务规则设定上下限。例如,年收入低于 1 万元或高于 1000 万元的记录应标记为可疑,而非直接删除,需人工复核。

4.3 重复记录处理

重复记录会导致模型对某些样本过度学习,造成过拟合。重复可能来源于数据合并时的重复导入、同一用户多次申请等。Spark 中通过 dropDuplicates() 方法可以快速去重,但需要谨慎选择去重键。

4.3.1 重复记录检测与去重

# 基于所有字段的完全重复检测
total_duplicates = df.count() - df.distinct().count()
print(f"完全重复记录数: {total_duplicates}")

# 基于业务键的部分重复检测(如用户ID + 申请日期)
from pyspark.sql.window import Window

window_spec = Window.partitionBy("user_id", "application_date").orderBy("application_timestamp")
df_with_dup_rank = df.withColumn("dup_rank", F.row_number().over(window_spec))

# 保留每个用户每天的最新一条记录
df_deduplicated = df_with_dup_rank.filter("dup_rank = 1").drop("dup_rank")
重复类型 检测方法 处理策略 适用场景
完全重复 df.count() - df.distinct().count() 直接删除 数据导入异常
部分重复(业务键相同) 窗口函数 + row_number 保留最新或最早记录 同一用户多次申请
近似重复(文本相似度) SimHash / MinHash 人工审核或聚类去重 地址、公司名称等文本字段

4.4 数据分布概览

数据分布概览帮助我们理解每个特征的取值范围、集中趋势和离散程度。Spark 的 summary() 函数可以快速输出数值型字段的统计量,但对于信用评分场景,我们还需要关注分布的形状和偏态。

4.4.1 分布统计与可视化准备

# 使用 summary 快速获取统计量
df.select("loan_amount", "age", "income", "credit_score").summary().show()

# 自定义分位数统计
quantiles_list = [0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99]
for col_name in ["loan_amount", "income"]:
    quantiles = df.approxQuantile(col_name, quantiles_list, 0.01)
    print(f"{col_name} 分位数: {dict(zip(quantiles_list, quantiles))}")
注意: 信用评分数据通常呈现右偏分布(如收入、贷款金额),此时均值会远大于中位数。在后续特征工程中,建议对这类字段进行对数变换或 Box-Cox 变换,使其更接近正态分布。
数据质量初探全流程 原始信用评分数据 缺失值统计 每列缺失率计算 阈值判断(5%/50%) 异常值检测 IQR / Z-Score 方法 业务规则阈值 重复记录处理 完全重复 / 部分重复 窗口函数去重 数据分布概览 summary() / 分位数统计 偏态识别与变换决策

4.5 综合质量报告与决策

完成上述四个维度的探查后,建议生成一份综合数据质量报告,为后续的特征工程提供决策依据。以下是一个典型的报告模板:

字段名 缺失率 异常值比例 重复率 分布形态 综合评级 处理建议
loan_amount 0.5% 2.1% 0.0% 右偏 B 对数变换 + 异常值截断
income 12.3% 1.8% 0.1% 严重右偏 C 中位数填充 + 对数变换
age 0.0% 0.3% 0.0% 近似正态 A 无需处理
zip_code 56.8% — — — D 剔除该字段
最佳实践: 在信用评分项目中,建议将数据质量探查脚本封装为可复用的 Spark 作业,每次数据更新后自动运行并生成报告。同时,将质量规则(如缺失率阈值、异常值上下限)配置在外部文件中,便于业务人员调整。
数据质量决策流程 开始质量探查 缺失率 > 50% ? 剔除该字段 进行异常值检测 生成质量报告与处理策略

通过以上四个维度的系统探查,我们能够全面了解数据的健康状况,为后续的缺失值填充、异常值处理、特征构造和模型训练提供清晰的决策依据。在下一阶段,我们将基于这些质量报告,进行具体的特征工程操作。