跳转至

4 · 两项外部来源分如何共同区分风险?

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

本批实际结果

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

0train / TARGET / 来源分 2 固定 b1;坏率按已观测人数计算。

来源分 3 箱 全部人数 已观测 坏率 Δ 行基线 pp Lift
b3 10035 10035 3.6472% -4.5319 0.4498
b2 10546 10546 5.0730% -3.1062 0.6256
b1 10918 10918 7.8494% -0.3298 0.9680
b0 11520 11520 14.3490% 6.1698 1.7695

X 是 EXT_SOURCE_2,Y 是 EXT_SOURCE_3。它们是公开的外部来源分,没有在本例训练模型,也不能视为校准违约概率。 两轴切点只在 0train 拟合;方向结合官方说明与开发样本观察登记,关联不解释为因果。 保留小数切点精度、Missing 箱、Bad Rate、人数、Lift 和组合条件。 sample_count 是全样本数,observed_sample_count 是有效标签人数;Bad Rate 分母使用后者。 4_oot_undue 的人数与分布完整保留,监督指标为实际未观测状态。颜色或单格 Lift 不等于部署资格。

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

def _score_cross(inputs: HomeCreditInputs, output: Path) -> Report:
    """Train 固定外部来源分切点,五分区保留真实人数、金额与未观测状态。"""
    context = {**inputs.business_context, "score_directions": DIRECTIONS,
               "score_direction_basis": "official normalized external score; observed Train association only",
               "binning_origin": "0train only; frozen cutpoints"}
    initial = cross_scores(inputs.benchmark_df, score_x="EXT_SOURCE_2", score_y="EXT_SOURCE_3",
                           targets=["TARGET"], score_directions=DIRECTIONS, n_bins=4,
                           feature_metadata=inputs.feature_metadata, business_context=context)
    definitions = get_score_bin_definitions(initial)
    cross = cross_scores(
        inputs.df, score_x="EXT_SOURCE_2", score_y="EXT_SOURCE_3", targets=["TARGET"],
        score_directions=DIRECTIONS, bin_definitions=definitions, group_col="dataset_flag",
        amount_col="AMT_CREDIT", min_observed=30,
        feature_metadata=inputs.feature_metadata, business_context=context,
    )
    cross.save(output / "score-cross.marsreport", overwrite=True)
    policy = evaluate_score_policy(cross, POLICY, baseline=BASELINE)
    policy.save(output / "policy.marsreport", overwrite=True)
    policy.write_excel(str(output / "policy.xlsx"))
    write_score_cross_html(cross, output / "score-cross.html", policy_reports=[policy],
                           report_name="Home Credit:外部来源分 2 × 外部来源分 3")
    cross.write_excel(str(output / "score-cross.xlsx"))
    scope = {"group": "0train", "target": "TARGET"}
    queries = [
        _query(cross, "cells", "同一来源分 2 箱内的来源分 3 风险梯度", filters={**scope, "x_bin": "b1"}, sort_by="y_risk_rank", limit=8),
        _query(cross, "row_summary", "来源分 2 边际风险包含缺失箱", filters=scope, sort_by="x_risk_rank", limit=6),
        _query(cross, "column_summary", "来源分 3 的边际风险", filters=scope, sort_by="y_risk_rank", limit=6),
        _query(cross, "cells", "无标签比赛测试的分布和未观测状态", filters={"group": "4_oot_undue", "target": "TARGET"}, limit=6),
        _query(policy, "changes", "固定表达式与单来源分基线的聚合回放", limit=5),
    ]
    _case(output, 4, cross, "两个外部来源分联合后,哪些区间有风险差异?", queries, {
        "scope": scope, "x_bin": "b1", "normal_cells": [row for row in queries[0]["rows"] if row["y_bin"].startswith("b")],
        "status_counts": cross.get_table("cells").group_by("status").len().sort("status").to_dicts(),
        "input_row_count": inputs.df.height, "bad_rate_denominator": "observed_sample_count",
        "ratio_unit": "fraction; delta pp = 100 * delta_vs_row", "bin_definitions": definitions,
        "policy_report_id": policy.report_id,
    }, "外部来源分不是本例训练模型或校准违约概率;关联与历史留存回放不证明因果或利润。")
    return cross

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

from mars.reporting import load_report

report = load_report("score-cross.marsreport")
print(report.describe())
page = report.query_page("cells", features="EXT_SOURCE_2", limit=3)
print(page["reference"], page["data"])
实际查询结果与分页引用
{
  "case": 4,
  "report_id": "4178f2f6-3d7c-4583-b1da-9d4c0bce24ec",
  "consumer": "deterministic public API queries; no LLM",
  "question": "两个外部来源分联合后,哪些区间有风险差异?",
  "query": {
    "question": "同一来源分 2 箱内的来源分 3 风险梯度",
    "reference": {
      "report_id": "4178f2f6-3d7c-4583-b1da-9d4c0bce24ec",
      "table": "cells",
      "query": {
        "offset": 0,
        "limit": 8,
        "filters": {
          "group": "0train",
          "target": "TARGET",
          "x_bin": "b1"
        },
        "sort_by": "y_risk_rank"
      }
    },
    "total_rows": 6,
    "returned_rows": 6,
    "omitted_rows": 0,
    "next_offset": null,
    "rows": [
      {
        "group": "0train",
        "period": "Total",
        "target": "TARGET",
        "x_bin": "b1",
        "y_bin": "b3",
        "x_risk_rank": 3,
        "y_risk_rank": 1,
        "sample_count": 10035,
        "observed_sample_count": 10035,
        "bad_sample_count": 366,
        "tot_amt": 5924342241.0,
        "good_amt": 5704408129.5,
        "bad_amt": 219934111.5,
        "sample_status": "populated",
        "bad_rate": 0.03647234678624813,
        "observed_coverage": 1.0,
        "unweighted_ci_lower": 0.03297835412132557,
        "unweighted_ci_upper": 0.040321086042075176,
        "status": "valid",
        "weighted_ci_status": "not_requested",
        "unweighted_ci_status": "valid",
        "observed_amt": 5924342241.0,
        "amt_bad_rate": 0.03712380253421622,
        "scope_sample_count": 215374,
        "overall_bad_rate": 0.081091496652335,
        "overall_status": "valid",
        "sample_share": 0.046593367815985214,
        "lift_vs_overall": 0.449767833767043,
        "lift_status": "valid",
        "row_bad_rate": 0.08179178918375703,
        "delta_vs_row": -0.045319442397508895,
        "lift_vs_row": 0.44591696978663414
      },
      {
        "group": "0train",
        "period": "Total",
        "target": "TARGET",
        "x_bin": "b1",
        "y_bin": "b2",
        "x_risk_rank": 3,
        "y_risk_rank": 2,
        "sample_count": 10546,
        "observed_sample_count": 10546,
        "bad_sample_count": 535,
        "tot_amt": 6358590031.5,
        "good_amt": 6046360078.5,
        "bad_amt": 312229953.0,
        "sample_status": "populated",
        "bad_rate": 0.05073013464820785,
        "observed_coverage": 1.0,
        "unweighted_ci_lower": 0.046703048462136236,
        "unweighted_ci_upper": 0.05508440142630338,
        "status": "valid",
        "weighted_ci_status": "not_requested",
        "unweighted_ci_status": "valid",
        "observed_amt": 6358590031.5,
        "amt_bad_rate": 0.04910364584809449,
        "scope_sample_count": 215374,
        "overall_bad_rate": 0.081091496652335,
        "overall_status": "valid",
        "sample_share": 0.04896598475210564,
        "lift_vs_overall": 0.6255912980087671,
        "lift_status": "valid",
        "row_bad_rate": 0.08179178918375703,
        "delta_vs_row": -0.031061654535549175,
        "lift_vs_row": 0.6202350523746987
      },
      {
        "group": "0train",
        "period": "Total",
        "target": "TARGET",
        "x_bin": "b1",
        "y_bin": "b1",
        "x_risk_rank": 3,
        "y_risk_rank": 3,
        "sample_count": 10918,
        "observed_sample_count": 10918,
        "bad_sample_count": 857,
        "tot_amt": 6419354265.0,
        "good_amt": 5939606803.5,
        "bad_amt": 479747461.5,
        "sample_status": "populated",
        "bad_rate": 0.07849422971240154,
        "observed_coverage": 1.0,
        "unweighted_ci_lower": 0.07359638613448766,
        "unweighted_ci_upper": 0.0836885795279036,
        "status": "valid",
        "weighted_ci_status": "not_requested",
        "unweighted_ci_status": "valid",
        "observed_amt": 6419354265.0,
        "amt_bad_rate": 0.07473453585755638,
        "scope_sample_count": 215374,
        "overall_bad_rate": 0.081091496652335,
        "overall_status": "valid",
        "sample_share": 0.05069321273691346,
        "lift_vs_overall": 0.9679711554582748,
        "lift_status": "valid",
        "row_bad_rate": 0.08179178918375703,
        "delta_vs_row": -0.003297559471355488,
        "lift_vs_row": 0.9596834901857074
      },
      {
        "group": "0train",
        "period": "Total",
        "target": "TARGET",
        "x_bin": "b1",
        "y_bin": "b0",
        "x_risk_rank": 3,
        "y_risk_rank": 4,
        "sample_count": 11520,
        "observed_sample_count": 11520,
        "bad_sample_count": 1653,
        "tot_amt": 6454188297.0,
        "good_amt": 5536718383.5,
        "bad_amt": 917469913.5,
        "sample_status": "populated",
        "bad_rate": 0.14348958333333334,
        "observed_coverage": 1.0,
        "unweighted_ci_lower": 0.13720664467868499,
        "unweighted_ci_upper": 0.1500102066332742,
        "status": "valid",
        "weighted_ci_status": "not_requested",
        "unweighted_ci_status": "valid",
        "observed_amt": 6454188297.0,
        "amt_bad_rate": 0.14215109186176877,
        "scope_sample_count": 215374,
        "overall_bad_rate": 0.081091496652335,
        "overall_status": "valid",
        "sample_share": 0.05348835049727451,
        "lift_vs_overall": 1.7694775563030825,
        "lift_status": "valid",
        "row_bad_rate": 0.08179178918375703,
        "delta_vs_row": 0.06169779414957631,
        "lift_vs_row": 1.7543274791429655
      },
      {
        "group": "0train",
        "period": "Total",
        "target": "TARGET",
        "x_bin": "b1",
        "y_bin": "invalid",
        "x_risk_rank": 3,
        "y_risk_rank": null,
        "sample_count": 0,
        "observed_sample_count": 0,
        "bad_sample_count": 0,
        "tot_amt": 0.0,
        "good_amt": 0.0,
        "bad_amt": 0.0,
        "sample_status": "empty",
        "bad_rate": null,
        "observed_coverage": null,
        "unweighted_ci_lower": null,
        "unweighted_ci_upper": null,
        "status": "empty",
        "weighted_ci_status": "not_requested",
        "unweighted_ci_status": "unavailable",
        "observed_amt": 0.0,
        "amt_bad_rate": null,
        "scope_sample_count": 215374,
        "overall_bad_rate": 0.081091496652335,
        "overall_status": "valid",
        "sample_share": 0.0,
        "lift_vs_overall": null,
        "lift_status": "empty",
        "row_bad_rate": 0.08179178918375703,
        "delta_vs_row": null,
        "lift_vs_row": null
      },
      {
        "group": "0train",
        "period": "Total",
        "target": "TARGET",
        "x_bin": "b1",
        "y_bin": "missing",
        "x_risk_rank": 3,
        "y_risk_rank": null,
        "sample_count": 10715,
        "observed_sample_count": 10715,
        "bad_sample_count": 984,
        "tot_amt": 5810879353.5,
        "good_amt": 5299043229.0,
        "bad_amt": 511836124.5,
        "sample_status": "populated",
        "bad_rate": 0.0918338777414839,
        "observed_coverage": 1.0,
        "unweighted_ci_lower": 0.08651108538621521,
        "unweighted_ci_upper": 0.09744923032477393,
        "status": "valid",
        "weighted_ci_status": "not_requested",
        "unweighted_ci_status": "valid",
        "observed_amt": 5810879353.5,
        "amt_bad_rate": 0.08808238708169215,
        "scope_sample_count": 215374,
        "overall_bad_rate": 0.081091496652335,
        "overall_status": "valid",
        "sample_share": 0.049750666282838224,
        "lift_vs_overall": 1.1324723495387548,
        "lift_status": "valid",
        "row_bad_rate": 0.08179178918375703,
        "delta_vs_row": 0.01004208855772687,
        "lift_vs_row": 1.1227762426759718
      }
    ]
  },
  "unavailable": "外部来源分不是本例训练模型或校准违约概率;关联与历史留存回放不证明因果或利润。"
}

运行与下载

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

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