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。文件缺失会明确失败,正式入口不回退合成数据。 数据下载与输入准备 · 完整共享源码 · 共享包 · 来源和哈希 · 案例索引