4. 数据质量检查:缺失值、异常值、重复值检测与可视化报告

在金融特征回放评审中,数据质量直接决定后续建模与回测的可靠性。缺失值、异常值与重复值是三类最常见的“脏数据”来源。本章构建一套基于 Python 的自动化检测与可视化报告工具链,帮助分析师快速定位问题、量化影响并生成可复用的质量报告。

核心原则:金融时序数据对完整性、一致性与分布稳定性要求极高。任何缺失或异常都可能导致回放信号失真或策略评估偏差。建议将质量检查作为特征流水线的第一道关卡。

4.1 三类质量问题的定义与金融影响

问题类型 定义 典型金融场景影响
缺失值 特征字段为空或 NaN 导致回放中断、统计量偏倚、模型无法计算
异常值 数值显著偏离正常分布(如 3σ 之外) 扭曲波动率估计、触发虚假交易信号
重复值 完全相同或关键字段重复的记录 放大交易频次、错误计算持仓周期

4.2 检测流程架构

下图展示了从原始特征表到质量报告的核心检测流水线。每个阶段均输出中间统计量,支持逐层下钻排查。

数据质量检测流水线 原始特征表 缺失值检测 (isnull + 比率) 异常值检测 (IQR / Z-Score) 重复值检测 (duplicated) 质量报告 (HTML/PDF)

4.3 核心检测函数实现

以下代码封装了三个独立的检测模块,每个模块返回统计 DataFrame,便于后续合并与可视化。

import pandas as pd
import numpy as np

def detect_missing(df, threshold=0.0):
    """检测缺失值,返回缺失比率 > threshold 的列"""
    missing_ratio = df.isnull().mean()
    missing_df = missing_ratio[missing_ratio > threshold].reset_index()
    missing_df.columns = ['column', 'missing_ratio']
    missing_df['missing_count'] = (df.isnull().sum()[missing_df['column']]).values
    return missing_df.sort_values('missing_ratio', ascending=False)

def detect_outliers_iqr(df, columns, multiplier=1.5):
    """基于 IQR 检测异常值,返回每列异常记录数"""
    result = []
    for col in columns:
        if df[col].dtype not in ['float64', 'int64']:
            continue
        Q1 = df[col].quantile(0.25)
        Q3 = df[col].quantile(0.75)
        IQR = Q3 - Q1
        lower = Q1 - multiplier * IQR
        upper = Q3 + multiplier * IQR
        outlier_mask = (df[col] < lower) | (df[col] > upper)
        result.append({
            'column': col,
            'outlier_count': outlier_mask.sum(),
            'outlier_ratio': outlier_mask.mean(),
            'lower_bound': lower,
            'upper_bound': upper
        })
    return pd.DataFrame(result)

def detect_duplicates(df, subset=None):
    """检测完全重复或指定子集重复"""
    total_dup = df.duplicated(subset=subset, keep=False).sum()
    first_dup = df.duplicated(subset=subset, keep='first').sum()
    return {
        'total_duplicate_rows': total_dup,
        'first_occurrence_removed': first_dup,
        'duplicate_groups': df.duplicated(subset=subset, keep=False).sum()
    }

4.4 可视化报告生成

将检测结果整合为一张综合报告图,包含缺失热力图、异常值箱线图与重复记录分布。以下 SVG 展示报告模板的布局结构。

质量报告可视化布局 缺失值热力图 列 × 行缺失模式 颜色越深 → 缺失率越高 异常值箱线图 IQR 上下界外的点标记为异常 重复记录分布 按分组统计重复频次

4.5 一键生成综合报告

将检测与可视化合并为 generate_quality_report() 函数,输出包含表格与图表的 HTML 报告。

def generate_quality_report(df, numeric_cols, output_path='quality_report.html'):
    """生成数据质量综合报告"""
    # 1. 执行检测
    missing_df = detect_missing(df, threshold=0.0)
    outlier_df = detect_outliers_iqr(df, numeric_cols)
    dup_info = detect_duplicates(df)

    # 2. 构建 HTML 片段
    html_parts = []
    html_parts.append("<h2>数据质量报告</h2>")

    # 缺失值表格
    html_parts.append("<h3>缺失值统计</h3>")
    html_parts.append(missing_df.to_html(classes='table table-striped'))

    # 异常值表格
    html_parts.append("<h3>异常值统计 (IQR)</h3>")
    html_parts.append(outlier_df.to_html(classes='table table-striped'))

    # 重复值摘要
    html_parts.append("<h3>重复值摘要</h3>")
    html_parts.append(f"<p>总重复行数: {dup_info['total_duplicate_rows']}</p>")
    html_parts.append(f"<p>去重后保留首行: {dup_info['first_occurrence_removed']}</p>")

    # 3. 写入文件
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write('\n'.join(html_parts))
    print(f"报告已生成: {output_path}")
    return output_path
实战建议:在金融特征回放场景中,建议对缺失率超过 5% 的列进行插补或剔除;异常值检测应结合业务逻辑(如价格不能为负、成交量不能为 0);重复值需按时间戳 + 证券代码去重,避免误删合法多笔交易。

4.6 质量检查清单

  • 缺失值:检查每列缺失比率;对时序数据检查缺失是否连续;对关键特征(如收盘价)要求缺失率 < 1%。
  • 异常值:使用 IQR 或 Z-Score 标记;对金融数据建议使用 3σ 或 5σ 阈值;检查异常值是否集中在特定时间段。
  • 重复值:按主键(时间 + 代码)去重;记录重复组数;检查是否因数据源合并导致重复。
  • 报告输出:每次回放前自动生成质量报告;将报告纳入特征版本管理;设置质量阈值自动阻断流水线。
注意:不要一次性对所有列使用相同的异常检测参数。金融特征如收益率、波动率、换手率分布差异巨大,建议按特征类型分组设定检测参数,避免误杀正常极端行情数据。