跳转至

2 · 哪些特征有区分度,而且足够稳定?

本批保留 Home Credit 两张 application 表的全部 356,255 条记录。TARGET 来自原比赛训练表,比赛测试表的 48,744 条记录保持 null。APPLY_DATE 是按 ID 稳定散列生成的演示日期。0train 用于拟合和 PSI 基准,1val 用于验证或筛选,2test 与 3oot 用于固定结果的独立复核,4_oot_undue 仅表示比赛未公开标签。日期、元数据和角色映射见输入方案与分区说明。

本批实际结果

打开报告 · 有限查询与结论

TARGET;KS 为 0–100 点,PSI 无量纲。0train 拟合,3oot 的日期为模拟日期。

字段 Train KS Val KS Test KS OOT KS 无标签 PSI
EXT_SOURCE_2 21.1206 21.5852 21.8119 21.9017 0.0067
EXT_SOURCE_3 22.0037 23.3587 21.3300 23.0991 0.0051
AMT_INCOME_TOTAL 3.7852 3.9803 3.2401 3.0711 0.0196
DAYS_BIRTH 11.3724 13.4295 12.3029 12.7862 0.0004

只在 0train 拟合固定分箱,并以它作为 PSI 参考分布;五组复用同一分箱。 主图是 EXT_SOURCE_2 的 MARS 原生趋势图,完整展示一次。EXT_SOURCE_3、AMT_INCOME_TOTAL、DAYS_BIRTH 可在报告中继续查询。 完整候选 fcols 与有限展示字段分别记录在 manifest;有限图表不代表只分析了这些字段。 无标签组保留箱占比与适用 PSI,IV、KS、AUC 和已观测 Bad Rate 按实际未观测状态解释。 count、observed_count 与未观测人数以报告字段为准;本例不设权重,KS 为 0–100,Bad Rate 为 0–1,IV/PSI 无量纲。 模拟月趋势另存 binning-time.marsreport;模拟时间差异不能证明真实业务 OOT。

EXT_SOURCE_2 原生分箱图,TARGET 为官方标签,APPLY_DATE 为模拟日期

下载 300 dpi PNG · 查看 SVG · 原生图参数与查询来源

图由 MarsBinningReport.save_risk_trend_images() 直接导出,保留原生布局、坐标与标签。AMT_CREDIT 是贷款金额,使用公开数据的原始金额单位,不解释为利润或损失。

从报告查看真实表与图,按实际分母判断。完整可运行代码使用共享准备入口,复用同一宽表与固定参考样本。

def _binning(inputs: HomeCreditInputs, output: Path) -> None:
    """Train 决定所有固定切点与参考分布,五分区和模拟月趋势复用。"""
    display = [name for name in DEFAULT_DISPLAY_FEATURES if name in inputs.fcols]
    run = profile_risk(
        inputs.df, target="TARGET", features=inputs.fcols, benchmark_df=inputs.benchmark_df,
        method="quantile", n_bins=4, group_col="dataset_flag", time_col="APPLY_DATE",
        time_grain="month", amount_col="AMT_CREDIT",
        feature_metadata=inputs.feature_metadata, business_context=inputs.business_context,
        n_jobs=1, batch_size=30,
    )
    report = run.report
    report.save(output / "binning.marsreport", overwrite=True)
    report.write_html(str(output / "binning.html"), max_plots=len(display), include_charts=True,
                      chart_embed_mode="inline", report_name="Home Credit 固定分箱与五分区风险")
    load_report(output / "binning.marsreport").write_excel(str(output / "binning.xlsx"))
    time_report = MarsBinEvaluator().evaluate(
        inputs.df, target="TARGET", features=display, binner=run.binner,
        benchmark_df=inputs.benchmark_df, time_col="APPLY_DATE", time_grain="month",
        amount_col="AMT_CREDIT", feature_metadata=inputs.feature_metadata,
        business_context=inputs.business_context,
    ).report
    time_report.save(output / "binning-time.marsreport", overwrite=True)
    time_report.write_html(str(output / "binning-time.html"), max_plots=4, chart_embed_mode="inline")
    load_report(output / "binning-time.marsreport").write_excel(str(output / "binning-time.xlsx"))
    # 特殊码只应用于 DAYS_EMPLOYED 单字段,完整候选报告同时保留原始未改写值。
    employment = profile_risk(
        inputs.df, target="TARGET", features=["DAYS_EMPLOYED"], benchmark_df=inputs.benchmark_df,
        method="quantile", n_bins=4, special_values=[365243], group_col="dataset_flag",
        time_col="APPLY_DATE", time_grain="month",
        amount_col="AMT_CREDIT", feature_metadata=inputs.feature_metadata,
        business_context={**inputs.business_context, "special_value_processing": {"DAYS_EMPLOYED": [365243]}}, n_jobs=1,
    ).report
    employment.save(output / "days-employed-special.marsreport", overwrite=True)
    employment.write_html(str(output / "days-employed-special.html"), max_plots=1)
    load_report(output / "days-employed-special.marsreport").write_excel(str(output / "days-employed-special.xlsx"))
    actual_parameters = {"features": "EXT_SOURCE_2", "target": "TARGET", "dpi": 300, "show_risk": "both"}
    for image_format in ("png", "svg"):
        for native, stem in ((report, "binning-native-home-credit"), (time_report, "binning-native-time-home-credit")):
            generated = native.save_risk_trend_images(output, **actual_parameters,
                image_format=image_format, filename_prefix="native-export")
            shutil.copyfile(generated[0], output / f"{stem}.{image_format}")
            generated[0].unlink()
    queries = [
        _query(report, "summary", "外部来源分 2 与 3 的已观测区分度", features=list(DIRECTIONS), limit=2),
        _query(report, "detail", "主图五组固定箱人数和已观测坏率", features="EXT_SOURCE_2", limit=30),
        _query(report, "calculation_status", "无标签组监督指标真实状态", filters={"group": "4_oot_undue"}, features=list(DIRECTIONS), limit=10),
        _query(report, "detail", "收入与年龄天数原始分析入口", features=["AMT_INCOME_TOTAL", "DAYS_BIRTH"], limit=8),
    ]
    metrics: dict[str, Any] = {}
    for metric in ("iv", "ks", "auc", "psi"):
        evidence = _query(report, f"trend.{metric}", f"五分区 {metric} 固定分箱比较", features=FEATURES, limit=4)
        queries.append(evidence)
        metrics[metric] = evidence["rows"]
    _write_json(output / "binning-native-evidence.json", {
        "report_id": report.report_id, "time_report_id": time_report.report_id,
        "feature": "EXT_SOURCE_2", "target": "TARGET", "chart_png": "binning-native-home-credit.png",
        "chart_svg": "binning-native-home-credit.svg", "source_api": "MarsBinningReport.save_risk_trend_images",
        "parameters": actual_parameters, "layout": "existing native figure; no crop/redraw",
        "group": "dataset_flag", "time_col": "APPLY_DATE", "time_grain": "month",
        "scope": "five partitions; separate simulated monthly view", "queries": queries[:3],
    })
    _case(output, 2, report, "固定分箱怎样比较五分区与无标签分布?", queries, {
        "summary_rows": queries[0]["rows"], "status_rows": queries[2]["rows"], "split_metrics": metrics,
        "reference_row_count": inputs.benchmark_df.height, "targets": ["TARGET"],
        "candidate_features": inputs.fcols, "analyzed_features": inputs.fcols, "display_features": display,
        "primary_feature": "EXT_SOURCE_2", "time_report_id": time_report.report_id,
        "units": {"ks": "0-100 percentage points", "bad_rate": "fraction", "iv": "dimensionless", "psi": "dimensionless"},
    }, "模拟日期不能证明真实跨期泛化;无标签组只有分布/PSI,没有可观测坏率或 KS。")

新进程按目录读取已保存统计;实际有限 JSON 中的引用可重放。

from mars.reporting import load_report

report = load_report("binning.marsreport")
print(report.describe())
page = report.query_page("summary", features="EXT_SOURCE_2", limit=3)
print(page["reference"], page["data"])
实际查询结果与分页引用
{
  "case": 2,
  "report_id": "d73396cc-4ad6-493e-a69a-0cd2a0a435e6",
  "consumer": "deterministic public API queries; no LLM",
  "question": "固定分箱怎样比较五分区与无标签分布?",
  "query": {
    "question": "外部来源分 2 与 3 的已观测区分度",
    "reference": {
      "report_id": "d73396cc-4ad6-493e-a69a-0cd2a0a435e6",
      "table": "summary",
      "query": {
        "offset": 0,
        "limit": 2,
        "features": [
          "EXT_SOURCE_2",
          "EXT_SOURCE_3"
        ]
      }
    },
    "total_rows": 2,
    "returned_rows": 2,
    "omitted_rows": 0,
    "next_offset": null,
    "rows": [
      {
        "feature": "EXT_SOURCE_3",
        "display_name": "外部来源分 3",
        "dtype": "Float64",
        "data_source": "Home Credit 2018 application_{train|test}.csv",
        "iv": 0.2917010188102722,
        "ks": 22.186461924601865,
        "auc": 0.6482176971618914,
        "psi_max": 0.00506146451724824,
        "rc_min": 1.0,
        "lift_min": 0.439180232030532,
        "lift_max": 1.8693434599007182,
        "missing": 0.19545830935706138,
        "missing_min": 0.17782701460692235,
        "missing_max": 0.19945768755745727,
        "mono": -1.0
      },
      {
        "feature": "EXT_SOURCE_2",
        "display_name": "外部来源分 2",
        "dtype": "Float64",
        "data_source": "Home Credit 2018 application_{train|test}.csv",
        "iv": 0.2703412175178528,
        "ks": 21.25443262874279,
        "auc": 0.6414238444445256,
        "psi_max": 0.006740861911165587,
        "rc_min": 1.0,
        "lift_min": 0.4715535313877019,
        "lift_max": 1.7708857175047088,
        "missing": 0.0018750614026469777,
        "missing_min": 0.00016412276382733948,
        "missing_max": 0.0028283485045512732,
        "mono": -1.0
      }
    ]
  },
  "unavailable": "模拟日期不能证明真实跨期泛化;无标签组只有分布/PSI,没有可观测坏率或 KS。"
}

运行与下载

python docs/snippets/task_cases.py --case 2 --data-dir examples/home-credit-2018/data/raw_data --output-dir output/task-cases

首次生成全部关联证据用 --case all。文件缺失会明确失败,正式入口不回退合成数据。 数据下载与输入准备 · 完整共享源码 · 共享包 · 来源和哈希 · 案例索引