SuperModelingFactory 0.6.6__tar.gz → 0.6.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/Feature_Screen.py +199 -5
- supermodelingfactory-0.6.8/Modeling_Tool/Feature/Screen_Gates.py +491 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/Weighted_Screen.py +234 -13
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/LRM_Tool.py +31 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/LRM_Tool.pyi +1 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/_common.py +15 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/credit_model.py +80 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/feature_validation.py +297 -6
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/field_meta.py +41 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/sample_analysis.py +188 -19
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/screening_artifact.py +46 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +489 -55
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +9 -4
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/__init__.py +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/PKG-INFO +2 -2
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/README.md +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/SuperModelingFactory.egg-info/PKG-INFO +2 -2
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/SuperModelingFactory.egg-info/SOURCES.txt +1 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/pyproject.toml +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/setup.py +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/ExcelMaster/ExcelFormatTool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/ExcelMaster/ExcelMaster.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/ExcelMaster/Template.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/ExcelMaster/Utility.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/ExcelMaster/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/LICENSE +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/MANIFEST.in +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Binning_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Proc_Compare.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Slope_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/kDataFrame.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Core/utils.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/evaluate_model.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Explainability/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/ODPS_Distribution_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/ODPS_Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/Backward_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/GBM_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Model/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Sample/Sample_Split.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Sample/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/UAT/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Master.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/_utils/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/_utils/nan_guard.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/_utils/robust.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/_utils/sentinels.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/ref_font/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/ref_font/simsun.ttc +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Report/Report_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Report/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/SuperModelingFactory.egg-info/requires.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/SuperModelingFactory.egg-info/top_level.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/requirements.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/setup.cfg +0 -0
{supermodelingfactory-0.6.6 → supermodelingfactory-0.6.8}/Modeling_Tool/Feature/Feature_Screen.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import warnings
|
|
5
6
|
from dataclasses import dataclass, field
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Any, Literal, Mapping
|
|
@@ -14,16 +15,20 @@ from Modeling_Tool._utils.sentinels import SMF_MISSING_BIN
|
|
|
14
15
|
|
|
15
16
|
from .Weighted_Screen import (
|
|
16
17
|
WeightedScreenResult,
|
|
18
|
+
_DROPPED_DETAIL_COLS,
|
|
17
19
|
_apply_missing_rate_stage,
|
|
18
20
|
_apply_stage_keep,
|
|
19
21
|
_corr_dedup_weighted,
|
|
22
|
+
_gate_ranking_iv_map,
|
|
23
|
+
_iv_band_keep,
|
|
20
24
|
_legacy_unweighted_screen,
|
|
21
25
|
_psi_from_distributions,
|
|
22
26
|
_resolve_splits,
|
|
23
27
|
_summary_row,
|
|
28
|
+
_unit_weight_floored_iv,
|
|
24
29
|
_weighted_bin_distribution,
|
|
25
30
|
_weighted_corr_for_screen,
|
|
26
|
-
|
|
31
|
+
_weighted_iv_detail,
|
|
27
32
|
_weighted_screen_impl,
|
|
28
33
|
)
|
|
29
34
|
|
|
@@ -37,6 +42,14 @@ _MONOTONE_INIT_KEYS = frozenset({
|
|
|
37
42
|
"missing_woe",
|
|
38
43
|
"special_values",
|
|
39
44
|
"bin_label_decimals",
|
|
45
|
+
"min_bad_count",
|
|
46
|
+
"min_good_count",
|
|
47
|
+
"small_bin_policy",
|
|
48
|
+
"monotone_direction",
|
|
49
|
+
"reference_target",
|
|
50
|
+
"direction_conflict_policy",
|
|
51
|
+
"missing_bin_strategy",
|
|
52
|
+
"refine_min_n_bins_policy",
|
|
40
53
|
})
|
|
41
54
|
_MONOTONE_FIT_KEYS = frozenset({"chi2_binning", "chi2_p", "chi2_init_size", "n_jobs"})
|
|
42
55
|
|
|
@@ -50,6 +63,7 @@ class FeatureScreenConfig:
|
|
|
50
63
|
psi_use_woe_bins: bool = False
|
|
51
64
|
iv_enabled: bool = True
|
|
52
65
|
iv_threshold: float = 0.02
|
|
66
|
+
iv_upper_threshold: float | None = None
|
|
53
67
|
iv_bins: int = 10
|
|
54
68
|
iv_min_bin_prop: float = 0.05
|
|
55
69
|
iv_equal_freq: bool = True
|
|
@@ -77,6 +91,28 @@ class FeatureScreenConfig:
|
|
|
77
91
|
content: float = 1e-6
|
|
78
92
|
precision: int = 5
|
|
79
93
|
min_bin_prop: float = 0.05
|
|
94
|
+
# --- Post-corr selection gates (G03/G04/G05/G06); all legacy-off ------
|
|
95
|
+
# G03 group stability (requires SelectionEvidence — FVP only)
|
|
96
|
+
monthly_iv_min: float | None = None
|
|
97
|
+
monthly_iv_cv_max: float | None = None
|
|
98
|
+
direction_consistency_min: float | None = None
|
|
99
|
+
min_group_n: int | None = None
|
|
100
|
+
insufficient_group_policy: Literal["keep_warn", "drop", "raise"] = "keep_warn"
|
|
101
|
+
# G04 multi-target joint gate (requires SelectionEvidence — FVP only)
|
|
102
|
+
target_rules: Literal["all", "any", "min_pass_count"] | None = None
|
|
103
|
+
min_pass_count: int | None = None
|
|
104
|
+
per_target_iv_range: Any = None # (low, high) or {target: (low, high)}
|
|
105
|
+
direction_reference_target: str | None = None
|
|
106
|
+
# G05 hard top-N truncation
|
|
107
|
+
max_selected_features: int | None = None
|
|
108
|
+
min_selected_features: int | None = None
|
|
109
|
+
ranking_metric: str = "iv"
|
|
110
|
+
tie_breaker: str = "name"
|
|
111
|
+
# G06 VIF gate (needs the optional statsmodels extra)
|
|
112
|
+
vif_enabled: bool = False
|
|
113
|
+
vif_threshold: float = 10.0
|
|
114
|
+
vif_min_features: int = 2
|
|
115
|
+
vif_tie_break_metric: str = "iv"
|
|
80
116
|
|
|
81
117
|
|
|
82
118
|
def screen_config_from_mapping(
|
|
@@ -100,6 +136,9 @@ def screen_config_from_mapping(
|
|
|
100
136
|
psi_use_woe_bins=bool(cfg.get("psi_use_woe_bins", False)),
|
|
101
137
|
iv_enabled=bool(cfg.get("iv_enabled", True)),
|
|
102
138
|
iv_threshold=float(cfg.get("iv_threshold", 0.02)),
|
|
139
|
+
iv_upper_threshold=(
|
|
140
|
+
float(cfg["iv_upper_threshold"]) if cfg.get("iv_upper_threshold") is not None else None
|
|
141
|
+
),
|
|
103
142
|
iv_bins=iv_nbins,
|
|
104
143
|
iv_min_bin_prop=float(cfg.get("iv_min_bin_prop", 0.05)),
|
|
105
144
|
iv_equal_freq=bool(cfg.get("iv_equal_freq", True)),
|
|
@@ -125,6 +164,23 @@ def screen_config_from_mapping(
|
|
|
125
164
|
content=float(cfg.get("content", 1e-6)),
|
|
126
165
|
precision=int(cfg.get("precision", 5)),
|
|
127
166
|
min_bin_prop=float(cfg.get("min_bin_prop", cfg.get("iv_min_bin_prop", 0.05))),
|
|
167
|
+
monthly_iv_min=cfg.get("monthly_iv_min"),
|
|
168
|
+
monthly_iv_cv_max=cfg.get("monthly_iv_cv_max"),
|
|
169
|
+
direction_consistency_min=cfg.get("direction_consistency_min"),
|
|
170
|
+
min_group_n=cfg.get("min_group_n"),
|
|
171
|
+
insufficient_group_policy=str(cfg.get("insufficient_group_policy", "keep_warn")), # type: ignore[arg-type]
|
|
172
|
+
target_rules=cfg.get("target_rules"),
|
|
173
|
+
min_pass_count=cfg.get("min_pass_count"),
|
|
174
|
+
per_target_iv_range=cfg.get("per_target_iv_range"),
|
|
175
|
+
direction_reference_target=cfg.get("direction_reference_target"),
|
|
176
|
+
max_selected_features=cfg.get("max_selected_features"),
|
|
177
|
+
min_selected_features=cfg.get("min_selected_features"),
|
|
178
|
+
ranking_metric=str(cfg.get("ranking_metric", "iv")),
|
|
179
|
+
tie_breaker=str(cfg.get("tie_breaker", "name")),
|
|
180
|
+
vif_enabled=bool(cfg.get("vif_enabled", False)),
|
|
181
|
+
vif_threshold=float(cfg.get("vif_threshold", 10.0)),
|
|
182
|
+
vif_min_features=int(cfg.get("vif_min_features", 2)),
|
|
183
|
+
vif_tie_break_metric=str(cfg.get("vif_tie_break_metric", "iv")),
|
|
128
184
|
)
|
|
129
185
|
|
|
130
186
|
|
|
@@ -213,12 +269,15 @@ def _woe_bins_unweighted_screen(
|
|
|
213
269
|
config: FeatureScreenConfig,
|
|
214
270
|
*,
|
|
215
271
|
prefit_woe_engine: Any | None = None,
|
|
272
|
+
selection_evidence: Any | None = None,
|
|
216
273
|
) -> FeatureScreenResult:
|
|
217
274
|
from Modeling_Tool import CorrelationFilter, PSICalculator, VarExtractionInsights
|
|
218
275
|
|
|
219
276
|
ins, oos, oot = splits["ins"], splits["oos"], splits["oot"]
|
|
220
277
|
current = list(feature_cols)
|
|
221
278
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, None)]
|
|
279
|
+
dropped_rows: list[dict] = []
|
|
280
|
+
stage_tables: dict[str, pd.DataFrame] = {}
|
|
222
281
|
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
223
282
|
ins,
|
|
224
283
|
current,
|
|
@@ -228,6 +287,7 @@ def _woe_bins_unweighted_screen(
|
|
|
228
287
|
on_empty_stage=config.on_empty_stage,
|
|
229
288
|
)
|
|
230
289
|
binner = _resolve_screening_binner(splits, current, target_col, config, prefit_woe_engine)
|
|
290
|
+
engine_fit_features = list(current)
|
|
231
291
|
|
|
232
292
|
psi_table = pd.DataFrame(columns=["var", "psi_ins_oos", "psi_ins_oot", "psi_max"])
|
|
233
293
|
if config.psi_enabled:
|
|
@@ -264,6 +324,7 @@ def _woe_bins_unweighted_screen(
|
|
|
264
324
|
summary_rows.append(_summary_row("psi", n_before, len(current), config.psi_threshold, None))
|
|
265
325
|
|
|
266
326
|
iv_table = pd.DataFrame(columns=["var", "iv_weighted", "n_bins", "missing_rate"])
|
|
327
|
+
iv_floored_map: dict[str, float] = {}
|
|
267
328
|
if config.iv_enabled:
|
|
268
329
|
use_binner = config.iv_use_woe_bins and binner is not None
|
|
269
330
|
vi = VarExtractionInsights(
|
|
@@ -290,7 +351,20 @@ def _woe_bins_unweighted_screen(
|
|
|
290
351
|
)
|
|
291
352
|
if iv is not None and not iv.empty:
|
|
292
353
|
iv_table = iv.rename(columns={"iv": "iv_weighted"})[["var", "iv_weighted", "n_bins", "missing_rate"]]
|
|
293
|
-
|
|
354
|
+
if config.iv_upper_threshold is not None:
|
|
355
|
+
gate_frame = iv[["var", "iv"]].copy()
|
|
356
|
+
gate_frame["iv_floored"] = _unit_weight_floored_iv(
|
|
357
|
+
ins, list(gate_frame["var"]), target_col,
|
|
358
|
+
iv_bins=config.iv_bins, min_bin_prop=config.iv_min_bin_prop,
|
|
359
|
+
content=config.content,
|
|
360
|
+
)
|
|
361
|
+
iv_floored_map = dict(zip(gate_frame["var"], gate_frame["iv_floored"]))
|
|
362
|
+
keep = _iv_band_keep(
|
|
363
|
+
gate_frame, "iv", config.iv_threshold, config.iv_upper_threshold,
|
|
364
|
+
dropped_rows, upper_col="iv_floored",
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
keep = _iv_band_keep(iv, "iv", config.iv_threshold, config.iv_upper_threshold, dropped_rows)
|
|
294
368
|
n_before = len(current)
|
|
295
369
|
current = _apply_stage_keep(
|
|
296
370
|
current, keep, "iv", summary_rows,
|
|
@@ -312,7 +386,23 @@ def _woe_bins_unweighted_screen(
|
|
|
312
386
|
current = cf.remove_highly_correlated(current, max_iterations=config.corr_max_iterations)
|
|
313
387
|
summary_rows.append(_summary_row("corr", n_before, len(current), config.corr_threshold, None))
|
|
314
388
|
|
|
389
|
+
from .Screen_Gates import apply_post_corr_gates
|
|
390
|
+
|
|
391
|
+
gate_iv_map = _gate_ranking_iv_map(iv_table, iv_floored_map, config.iv_upper_threshold)
|
|
392
|
+
current = apply_post_corr_gates(
|
|
393
|
+
ins, current, config, selection_evidence, gate_iv_map,
|
|
394
|
+
summary_rows, dropped_rows, stage_tables,
|
|
395
|
+
weight_col=None, on_empty_stage=config.on_empty_stage,
|
|
396
|
+
)
|
|
397
|
+
|
|
315
398
|
summary_rows.append(_summary_row("final", len(feature_cols), len(current), None, None))
|
|
399
|
+
engine_obj, engine_meta = _screen_engine_payload(
|
|
400
|
+
binner,
|
|
401
|
+
fit_features=engine_fit_features,
|
|
402
|
+
target_col=target_col,
|
|
403
|
+
weight_col=None,
|
|
404
|
+
summary_rows=summary_rows,
|
|
405
|
+
)
|
|
316
406
|
return FeatureScreenResult(
|
|
317
407
|
selected_features=list(current),
|
|
318
408
|
iv_table=iv_table,
|
|
@@ -321,6 +411,13 @@ def _woe_bins_unweighted_screen(
|
|
|
321
411
|
summary=pd.DataFrame(summary_rows),
|
|
322
412
|
missing_rate_table=missing_rate_table,
|
|
323
413
|
missing_rate_dropped=missing_rate_dropped,
|
|
414
|
+
woe_engine=engine_obj,
|
|
415
|
+
woe_engine_meta=engine_meta,
|
|
416
|
+
dropped_detail=(
|
|
417
|
+
pd.DataFrame(dropped_rows, columns=_DROPPED_DETAIL_COLS)
|
|
418
|
+
if dropped_rows else pd.DataFrame(columns=_DROPPED_DETAIL_COLS)
|
|
419
|
+
),
|
|
420
|
+
stage_tables=stage_tables,
|
|
324
421
|
)
|
|
325
422
|
|
|
326
423
|
|
|
@@ -330,6 +427,52 @@ def _bins_to_numpy(bins: Any) -> np.ndarray:
|
|
|
330
427
|
return np.asarray(bins, dtype=object)
|
|
331
428
|
|
|
332
429
|
|
|
430
|
+
def _screen_engine_payload(
|
|
431
|
+
binner: Any | None,
|
|
432
|
+
*,
|
|
433
|
+
fit_features: list[str],
|
|
434
|
+
target_col: str,
|
|
435
|
+
weight_col: str | None,
|
|
436
|
+
summary_rows: list[dict],
|
|
437
|
+
) -> tuple[Any | None, dict[str, Any]]:
|
|
438
|
+
"""Build the (woe_engine, woe_engine_meta) result payload (G00).
|
|
439
|
+
|
|
440
|
+
Monotone engines attach as-is — they hold only per-feature bin tables.
|
|
441
|
+
WOE_Master engines are never attached (they retain the full training
|
|
442
|
+
frame at ``self.train_data``); their woe_table + metadata travel instead.
|
|
443
|
+
"""
|
|
444
|
+
if binner is None:
|
|
445
|
+
return None, {}
|
|
446
|
+
engine_name = type(binner).__name__
|
|
447
|
+
meta: dict[str, Any] = {
|
|
448
|
+
"engine_name": engine_name,
|
|
449
|
+
"fit_features": list(fit_features),
|
|
450
|
+
"target_col": target_col,
|
|
451
|
+
"weight_col": weight_col,
|
|
452
|
+
"screen_stages": [row.get("stage") for row in summary_rows],
|
|
453
|
+
}
|
|
454
|
+
if engine_name == "WOE_Master":
|
|
455
|
+
from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
|
|
456
|
+
|
|
457
|
+
try:
|
|
458
|
+
meta["woe_table"] = as_woe_engine(binner).get_woe_table(varlist=list(fit_features))
|
|
459
|
+
except Exception as exc:
|
|
460
|
+
meta["woe_table"] = None
|
|
461
|
+
meta["woe_table_error"] = repr(exc)
|
|
462
|
+
meta["engine_attached"] = False
|
|
463
|
+
warnings.warn(
|
|
464
|
+
"screening WOE engine is a WOE_Master, which retains the full "
|
|
465
|
+
"training frame — attaching its woe_table and metadata only. "
|
|
466
|
+
"Use woe_engine='monotone' to get a reusable, serializable "
|
|
467
|
+
"screening engine on the result.",
|
|
468
|
+
UserWarning,
|
|
469
|
+
stacklevel=3,
|
|
470
|
+
)
|
|
471
|
+
return None, meta
|
|
472
|
+
meta["engine_attached"] = True
|
|
473
|
+
return binner, meta
|
|
474
|
+
|
|
475
|
+
|
|
333
476
|
def _weighted_woe_bins_screen(
|
|
334
477
|
splits: dict[str, pd.DataFrame],
|
|
335
478
|
feature_cols: list[str],
|
|
@@ -338,6 +481,7 @@ def _weighted_woe_bins_screen(
|
|
|
338
481
|
config: FeatureScreenConfig,
|
|
339
482
|
*,
|
|
340
483
|
prefit_woe_engine: Any | None = None,
|
|
484
|
+
selection_evidence: Any | None = None,
|
|
341
485
|
) -> FeatureScreenResult:
|
|
342
486
|
from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
|
|
343
487
|
|
|
@@ -346,6 +490,8 @@ def _weighted_woe_bins_screen(
|
|
|
346
490
|
y_ins = ins[target_col].astype(float).to_numpy()
|
|
347
491
|
current = list(feature_cols)
|
|
348
492
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, weight_col)]
|
|
493
|
+
dropped_rows: list[dict] = []
|
|
494
|
+
stage_tables: dict[str, pd.DataFrame] = {}
|
|
349
495
|
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
350
496
|
ins,
|
|
351
497
|
current,
|
|
@@ -369,6 +515,7 @@ def _weighted_woe_bins_screen(
|
|
|
369
515
|
categorical_features=config.categorical_features,
|
|
370
516
|
)
|
|
371
517
|
adapter = as_woe_engine(binner)
|
|
518
|
+
engine_fit_features = list(current)
|
|
372
519
|
|
|
373
520
|
# Assign fitted bins once per split. PSI and IV both consume these labels;
|
|
374
521
|
# doing it here avoids a full WOE transform for every feature and stage.
|
|
@@ -420,7 +567,9 @@ def _weighted_woe_bins_screen(
|
|
|
420
567
|
psi_table = pd.DataFrame(columns=["var", "psi_ins_oos", "psi_ins_oot", "psi_max"])
|
|
421
568
|
|
|
422
569
|
iv_records: list[dict] = []
|
|
570
|
+
iv_floored_map: dict[str, float] = {}
|
|
423
571
|
if config.iv_enabled:
|
|
572
|
+
floor_content = config.content if config.iv_upper_threshold is not None else None
|
|
424
573
|
for var in current:
|
|
425
574
|
if var not in ins.columns or ins[var].nunique(dropna=False) <= 1:
|
|
426
575
|
continue
|
|
@@ -431,12 +580,16 @@ def _weighted_woe_bins_screen(
|
|
|
431
580
|
else:
|
|
432
581
|
# Categorical/object feature: IV itself is computed from the
|
|
433
582
|
# already-assigned bins; ``x`` only feeds np.isfinite() inside
|
|
434
|
-
#
|
|
583
|
+
# _weighted_iv_detail to derive the weighted
|
|
435
584
|
# missing rate. A hard float cast crashes on string levels
|
|
436
585
|
# (e.g. '4.高中'), so encode observed -> 1.0 / missing -> NaN,
|
|
437
586
|
# matching the notna semantics of _missing_rate_for_series.
|
|
438
587
|
x_ins = np.where(x_series.notna().to_numpy(), 1.0, np.nan)
|
|
439
|
-
iv_val, n_b, miss =
|
|
588
|
+
iv_val, n_b, miss, iv_floored = _weighted_iv_detail(
|
|
589
|
+
y_ins, w_ins, bins_ins, x_ins, content=floor_content,
|
|
590
|
+
)
|
|
591
|
+
if floor_content is not None:
|
|
592
|
+
iv_floored_map[var] = iv_floored
|
|
440
593
|
iv_records.append({
|
|
441
594
|
"var": var,
|
|
442
595
|
"iv_weighted": iv_val,
|
|
@@ -447,7 +600,15 @@ def _weighted_woe_bins_screen(
|
|
|
447
600
|
columns=["var", "iv_weighted", "n_bins", "missing_rate"],
|
|
448
601
|
)
|
|
449
602
|
if not iv_table.empty:
|
|
450
|
-
|
|
603
|
+
if config.iv_upper_threshold is not None:
|
|
604
|
+
gate_frame = iv_table[["var", "iv_weighted"]].copy()
|
|
605
|
+
gate_frame["iv_floored"] = gate_frame["var"].map(iv_floored_map)
|
|
606
|
+
keep = _iv_band_keep(
|
|
607
|
+
gate_frame, "iv_weighted", config.iv_threshold,
|
|
608
|
+
config.iv_upper_threshold, dropped_rows, upper_col="iv_floored",
|
|
609
|
+
)
|
|
610
|
+
else:
|
|
611
|
+
keep = _iv_band_keep(iv_table, "iv_weighted", config.iv_threshold, config.iv_upper_threshold, dropped_rows)
|
|
451
612
|
n_before = len(current)
|
|
452
613
|
current = _apply_stage_keep(
|
|
453
614
|
current, keep, "iv", summary_rows,
|
|
@@ -479,7 +640,23 @@ def _weighted_woe_bins_screen(
|
|
|
479
640
|
)
|
|
480
641
|
summary_rows.append(_summary_row("corr", n_before, len(current), config.corr_threshold, weight_col))
|
|
481
642
|
|
|
643
|
+
from .Screen_Gates import apply_post_corr_gates
|
|
644
|
+
|
|
645
|
+
gate_iv_map = _gate_ranking_iv_map(iv_table, iv_floored_map, config.iv_upper_threshold)
|
|
646
|
+
current = apply_post_corr_gates(
|
|
647
|
+
ins, current, config, selection_evidence, gate_iv_map,
|
|
648
|
+
summary_rows, dropped_rows, stage_tables,
|
|
649
|
+
weight_col=weight_col, on_empty_stage=config.on_empty_stage,
|
|
650
|
+
)
|
|
651
|
+
|
|
482
652
|
summary_rows.append(_summary_row("final", len(feature_cols), len(current), None, weight_col))
|
|
653
|
+
engine_obj, engine_meta = _screen_engine_payload(
|
|
654
|
+
binner,
|
|
655
|
+
fit_features=engine_fit_features,
|
|
656
|
+
target_col=target_col,
|
|
657
|
+
weight_col=weight_col,
|
|
658
|
+
summary_rows=summary_rows,
|
|
659
|
+
)
|
|
483
660
|
return FeatureScreenResult(
|
|
484
661
|
selected_features=list(current),
|
|
485
662
|
iv_table=iv_table,
|
|
@@ -488,6 +665,13 @@ def _weighted_woe_bins_screen(
|
|
|
488
665
|
summary=pd.DataFrame(summary_rows),
|
|
489
666
|
missing_rate_table=missing_rate_table,
|
|
490
667
|
missing_rate_dropped=missing_rate_dropped,
|
|
668
|
+
woe_engine=engine_obj,
|
|
669
|
+
woe_engine_meta=engine_meta,
|
|
670
|
+
dropped_detail=(
|
|
671
|
+
pd.DataFrame(dropped_rows, columns=_DROPPED_DETAIL_COLS)
|
|
672
|
+
if dropped_rows else pd.DataFrame(columns=_DROPPED_DETAIL_COLS)
|
|
673
|
+
),
|
|
674
|
+
stage_tables=stage_tables,
|
|
491
675
|
)
|
|
492
676
|
|
|
493
677
|
|
|
@@ -499,6 +683,7 @@ def feature_screen(
|
|
|
499
683
|
weight_col: str | None = None,
|
|
500
684
|
config: FeatureScreenConfig | None = None,
|
|
501
685
|
prefit_woe_engine: Any | None = None,
|
|
686
|
+
selection_evidence: Any | None = None,
|
|
502
687
|
) -> FeatureScreenResult:
|
|
503
688
|
"""Run PSI -> IV -> correlation screening on pre-split INS/OOS/OOT frames."""
|
|
504
689
|
cfg = config or FeatureScreenConfig()
|
|
@@ -513,6 +698,7 @@ def feature_screen(
|
|
|
513
698
|
weight_col,
|
|
514
699
|
cfg,
|
|
515
700
|
prefit_woe_engine=prefit_woe_engine,
|
|
701
|
+
selection_evidence=selection_evidence,
|
|
516
702
|
)
|
|
517
703
|
return _weighted_screen_impl(
|
|
518
704
|
splits,
|
|
@@ -524,6 +710,7 @@ def feature_screen(
|
|
|
524
710
|
psi_compare_splits=list(cfg.psi_compare_splits),
|
|
525
711
|
iv_enabled=cfg.iv_enabled,
|
|
526
712
|
iv_threshold=cfg.iv_threshold,
|
|
713
|
+
iv_upper_threshold=cfg.iv_upper_threshold,
|
|
527
714
|
iv_bins=cfg.iv_bins,
|
|
528
715
|
min_bin_prop=cfg.min_bin_prop,
|
|
529
716
|
corr_enabled=cfg.corr_enabled,
|
|
@@ -538,6 +725,8 @@ def feature_screen(
|
|
|
538
725
|
prefit_woe_engine=prefit_woe_engine,
|
|
539
726
|
missing_rate_threshold=cfg.missing_rate_threshold,
|
|
540
727
|
missing_rate_ref=cfg.missing_rate_ref,
|
|
728
|
+
gates_config=cfg,
|
|
729
|
+
selection_evidence=selection_evidence,
|
|
541
730
|
)
|
|
542
731
|
|
|
543
732
|
if use_woe_bins or prefit_woe_engine is not None:
|
|
@@ -547,6 +736,7 @@ def feature_screen(
|
|
|
547
736
|
target_col,
|
|
548
737
|
cfg,
|
|
549
738
|
prefit_woe_engine=prefit_woe_engine,
|
|
739
|
+
selection_evidence=selection_evidence,
|
|
550
740
|
)
|
|
551
741
|
|
|
552
742
|
return _legacy_unweighted_screen(
|
|
@@ -558,6 +748,7 @@ def feature_screen(
|
|
|
558
748
|
psi_compare_splits=list(cfg.psi_compare_splits),
|
|
559
749
|
iv_enabled=cfg.iv_enabled,
|
|
560
750
|
iv_threshold=cfg.iv_threshold,
|
|
751
|
+
iv_upper_threshold=cfg.iv_upper_threshold,
|
|
561
752
|
iv_bins=cfg.iv_bins,
|
|
562
753
|
iv_min_bin_prop=cfg.iv_min_bin_prop,
|
|
563
754
|
corr_enabled=cfg.corr_enabled,
|
|
@@ -570,6 +761,9 @@ def feature_screen(
|
|
|
570
761
|
on_empty_stage=cfg.on_empty_stage,
|
|
571
762
|
missing_rate_threshold=cfg.missing_rate_threshold,
|
|
572
763
|
missing_rate_ref=cfg.missing_rate_ref,
|
|
764
|
+
gates_config=cfg,
|
|
765
|
+
selection_evidence=selection_evidence,
|
|
766
|
+
content=cfg.content,
|
|
573
767
|
)
|
|
574
768
|
|
|
575
769
|
|