跳转至

WOE 分箱引擎

0.1.4 起,特征筛选工具支持复用已拟合的 WOE 分箱引擎。这样 PSI、IV、KS、相关性去冗余与最终建模使用的是同一套分箱,不会再出现“筛选时一套分箱,建模时另一套分箱”的偏差。

flowchart TD
    A[原始特征] --> B{选择分箱引擎}
    B -->|WOE_Master| C1[fit WOE_Master]
    B -->|MonotoneWOEBinner| C2[fit MonotoneWOEBinner]
    C1 --> D[PSI / IV / 相关性筛选\n传入 binning_engine 或 woe_binner]
    C2 --> D
    D --> E[WOE transform]
    E --> F[模型训练]
    F --> G[监控 PSI\n复用同一分箱引擎]

为什么需要统一引擎

过去 PSICalculatorVarExtractionInsightsCorrelationFilter 会各自重新分箱或默认使用 WOE_Master。如果生产模型实际采用 MonotoneWOEBinner,筛选指标就可能和最终 WOE 编码不一致。

统一分箱引擎后:

  • PSI 使用训练期同一套分箱边界比较分布漂移。
  • IV / KS 使用同一套 WOE 分箱计算变量解释力。
  • 相关性去冗余时,保留变量的决策指标与最终建模一致。
  • 默认不传新参数时,旧行为保持不变。

引擎对比

维度 WOE_Master MonotoneWOEBinner
适用场景 快速探索、通用 WOE 编码 评分卡上线、强单调约束
单调性 依赖分箱结果 贪心合并到单调
类别特征 按既有逻辑自动处理 cate_feats + refine_cate()
持久化产物 mapping table get_final_bins()
转换方法 transform() apply_woe()
统一入口 as_woe_engine(woe) as_woe_engine(binner)

统一适配器

from Modeling_Tool import as_woe_engine

engine = as_woe_engine(binner)   # binner 可以是 WOE_Master 或 MonotoneWOEBinner
woe_table = engine.get_woe_table(features)
train_woe = engine.transform(train_df, features)

大多数用户不需要直接操作 adapter,只需要把已拟合对象传给筛选工具。

Monotone 路径示例

from Modeling_Tool import PSICalculator, VarExtractionInsights, CorrelationFilter
from Modeling_Tool.WOE.WOE_Monotone_Binner import MonotoneWOEBinner

features = ["age", "income", "utilization"]

binner = MonotoneWOEBinner(
    feature_cols=features,
    target_col="bad_flag",
    n_init_bins=20,
    min_bin_size=0.03,
    special_values=[-1, -100, -999999],
)
binner.fit(train_df, chi2_binning=True, chi2_p=0.95)

# 1) PSI:复用 Monotone 分箱
psi = PSICalculator(buckets=10, binning_engine=binner)
psi_table = psi.calculate(train_df, oot_df, features)
stable_features = psi_table.loc[psi_table["psi"] < 0.1, "var"].tolist()

# 2) IV / KS:复用同一 binner
insights = VarExtractionInsights(
    data=train_df,
    dep="bad_flag",
    plot_path="./iv_plots/",
    woe_engine="monotone",
    woe_binner=binner,
)
iv_report = insights.get_var_analysis_report(train_df, stable_features)
keep_by_iv = iv_report.loc[iv_report["iv"].between(0.02, 0.5), "var"].tolist()

# 3) 相关性:高相关变量保留谁,也用同一套 IV/KS 指标
keep_vars = CorrelationFilter(
    data=train_df,
    dep="bad_flag",
    corr_cutpoint=0.7,
    woe_engine="monotone",
    woe_binner=binner,
).remove_highly_correlated(keep_by_iv)

# 4) 建模转换
train_woe = binner.apply_woe(train_df)
oot_woe = binner.apply_woe(oot_df)

WOE_Master 路径示例

from Modeling_Tool import WOE_Master, PSICalculator, VarExtractionInsights

woe = WOE_Master(train_data=train_df, varlist=features, dep="bad_flag")
woe.fit(nbins=10, equal_freq=True)

psi_table = PSICalculator(binning_engine=woe).calculate(train_df, oot_df, features)

insights = VarExtractionInsights(
    data=train_df,
    dep="bad_flag",
    plot_path="./iv_plots/",
    woe_binner=woe,
)
iv_report = insights.get_var_analysis_report(train_df, features)

train_woe = woe.transform(train_df)

常见问题

不传 binning_engine 会怎样?

行为与旧版本一致:PSICalculator 仍按自身配置重新分箱,VarExtractionInsightsCorrelationFilter 仍走原有默认逻辑。

为什么 PSI 要复用建模分箱?

监控 PSI 的目标是检查线上样本相对训练样本在同一套特征映射下是否漂移。如果每次按当前数据重新分箱,PSI 会被分箱变化稀释,无法准确反映部署风险。

CorrelationFilter 应该传 raw 数据还是 WOE 数据?

推荐传 raw 数据,并传入同一个 woe_binner。相关性矩阵仍基于输入变量计算,但变量保留决策所需的 IV/KS 会复用该分箱引擎。