SuperModelingFactory 0.6.6__tar.gz → 0.6.7__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.7}/Modeling_Tool/Feature/Feature_Screen.py +169 -2
- supermodelingfactory-0.6.7/Modeling_Tool/Feature/Screen_Gates.py +470 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/Weighted_Screen.py +94 -4
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/LRM_Tool.py +31 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/LRM_Tool.pyi +1 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/_common.py +15 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/credit_model.py +80 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/feature_validation.py +256 -6
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/field_meta.py +41 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/sample_analysis.py +188 -19
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/screening_artifact.py +46 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +489 -55
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +9 -4
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/__init__.py +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/PKG-INFO +2 -2
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/README.md +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/SuperModelingFactory.egg-info/PKG-INFO +2 -2
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/SuperModelingFactory.egg-info/SOURCES.txt +1 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/pyproject.toml +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/setup.py +1 -1
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/ExcelMaster/ExcelFormatTool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/ExcelMaster/ExcelMaster.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/ExcelMaster/Template.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/ExcelMaster/Utility.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/ExcelMaster/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/LICENSE +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/MANIFEST.in +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Binning_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Proc_Compare.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Slope_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/kDataFrame.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Core/utils.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/evaluate_model.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Explainability/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/ODPS_Distribution_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/ODPS_Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Feature/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/Backward_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/GBM_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Model/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Sample/Sample_Split.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/Sample/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/UAT/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Master.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/_utils/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/_utils/nan_guard.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/_utils/robust.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/_utils/sentinels.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/ref_font/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Modeling_Tool/ref_font/simsun.ttc +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Report/Report_Tool.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/Report/__init__.py +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/SuperModelingFactory.egg-info/requires.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/SuperModelingFactory.egg-info/top_level.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/requirements.txt +0 -0
- {supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/setup.cfg +0 -0
{supermodelingfactory-0.6.6 → supermodelingfactory-0.6.7}/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,9 +15,11 @@ 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
|
+
_iv_band_keep,
|
|
20
23
|
_legacy_unweighted_screen,
|
|
21
24
|
_psi_from_distributions,
|
|
22
25
|
_resolve_splits,
|
|
@@ -37,6 +40,14 @@ _MONOTONE_INIT_KEYS = frozenset({
|
|
|
37
40
|
"missing_woe",
|
|
38
41
|
"special_values",
|
|
39
42
|
"bin_label_decimals",
|
|
43
|
+
"min_bad_count",
|
|
44
|
+
"min_good_count",
|
|
45
|
+
"small_bin_policy",
|
|
46
|
+
"monotone_direction",
|
|
47
|
+
"reference_target",
|
|
48
|
+
"direction_conflict_policy",
|
|
49
|
+
"missing_bin_strategy",
|
|
50
|
+
"refine_min_n_bins_policy",
|
|
40
51
|
})
|
|
41
52
|
_MONOTONE_FIT_KEYS = frozenset({"chi2_binning", "chi2_p", "chi2_init_size", "n_jobs"})
|
|
42
53
|
|
|
@@ -50,6 +61,7 @@ class FeatureScreenConfig:
|
|
|
50
61
|
psi_use_woe_bins: bool = False
|
|
51
62
|
iv_enabled: bool = True
|
|
52
63
|
iv_threshold: float = 0.02
|
|
64
|
+
iv_upper_threshold: float | None = None
|
|
53
65
|
iv_bins: int = 10
|
|
54
66
|
iv_min_bin_prop: float = 0.05
|
|
55
67
|
iv_equal_freq: bool = True
|
|
@@ -77,6 +89,28 @@ class FeatureScreenConfig:
|
|
|
77
89
|
content: float = 1e-6
|
|
78
90
|
precision: int = 5
|
|
79
91
|
min_bin_prop: float = 0.05
|
|
92
|
+
# --- Post-corr selection gates (G03/G04/G05/G06); all legacy-off ------
|
|
93
|
+
# G03 group stability (requires SelectionEvidence — FVP only)
|
|
94
|
+
monthly_iv_min: float | None = None
|
|
95
|
+
monthly_iv_cv_max: float | None = None
|
|
96
|
+
direction_consistency_min: float | None = None
|
|
97
|
+
min_group_n: int | None = None
|
|
98
|
+
insufficient_group_policy: Literal["keep_warn", "drop", "raise"] = "keep_warn"
|
|
99
|
+
# G04 multi-target joint gate (requires SelectionEvidence — FVP only)
|
|
100
|
+
target_rules: Literal["all", "any", "min_pass_count"] | None = None
|
|
101
|
+
min_pass_count: int | None = None
|
|
102
|
+
per_target_iv_range: Any = None # (low, high) or {target: (low, high)}
|
|
103
|
+
direction_reference_target: str | None = None
|
|
104
|
+
# G05 hard top-N truncation
|
|
105
|
+
max_selected_features: int | None = None
|
|
106
|
+
min_selected_features: int | None = None
|
|
107
|
+
ranking_metric: str = "iv"
|
|
108
|
+
tie_breaker: str = "name"
|
|
109
|
+
# G06 VIF gate (needs the optional statsmodels extra)
|
|
110
|
+
vif_enabled: bool = False
|
|
111
|
+
vif_threshold: float = 10.0
|
|
112
|
+
vif_min_features: int = 2
|
|
113
|
+
vif_tie_break_metric: str = "iv"
|
|
80
114
|
|
|
81
115
|
|
|
82
116
|
def screen_config_from_mapping(
|
|
@@ -100,6 +134,9 @@ def screen_config_from_mapping(
|
|
|
100
134
|
psi_use_woe_bins=bool(cfg.get("psi_use_woe_bins", False)),
|
|
101
135
|
iv_enabled=bool(cfg.get("iv_enabled", True)),
|
|
102
136
|
iv_threshold=float(cfg.get("iv_threshold", 0.02)),
|
|
137
|
+
iv_upper_threshold=(
|
|
138
|
+
float(cfg["iv_upper_threshold"]) if cfg.get("iv_upper_threshold") is not None else None
|
|
139
|
+
),
|
|
103
140
|
iv_bins=iv_nbins,
|
|
104
141
|
iv_min_bin_prop=float(cfg.get("iv_min_bin_prop", 0.05)),
|
|
105
142
|
iv_equal_freq=bool(cfg.get("iv_equal_freq", True)),
|
|
@@ -125,6 +162,23 @@ def screen_config_from_mapping(
|
|
|
125
162
|
content=float(cfg.get("content", 1e-6)),
|
|
126
163
|
precision=int(cfg.get("precision", 5)),
|
|
127
164
|
min_bin_prop=float(cfg.get("min_bin_prop", cfg.get("iv_min_bin_prop", 0.05))),
|
|
165
|
+
monthly_iv_min=cfg.get("monthly_iv_min"),
|
|
166
|
+
monthly_iv_cv_max=cfg.get("monthly_iv_cv_max"),
|
|
167
|
+
direction_consistency_min=cfg.get("direction_consistency_min"),
|
|
168
|
+
min_group_n=cfg.get("min_group_n"),
|
|
169
|
+
insufficient_group_policy=str(cfg.get("insufficient_group_policy", "keep_warn")), # type: ignore[arg-type]
|
|
170
|
+
target_rules=cfg.get("target_rules"),
|
|
171
|
+
min_pass_count=cfg.get("min_pass_count"),
|
|
172
|
+
per_target_iv_range=cfg.get("per_target_iv_range"),
|
|
173
|
+
direction_reference_target=cfg.get("direction_reference_target"),
|
|
174
|
+
max_selected_features=cfg.get("max_selected_features"),
|
|
175
|
+
min_selected_features=cfg.get("min_selected_features"),
|
|
176
|
+
ranking_metric=str(cfg.get("ranking_metric", "iv")),
|
|
177
|
+
tie_breaker=str(cfg.get("tie_breaker", "name")),
|
|
178
|
+
vif_enabled=bool(cfg.get("vif_enabled", False)),
|
|
179
|
+
vif_threshold=float(cfg.get("vif_threshold", 10.0)),
|
|
180
|
+
vif_min_features=int(cfg.get("vif_min_features", 2)),
|
|
181
|
+
vif_tie_break_metric=str(cfg.get("vif_tie_break_metric", "iv")),
|
|
128
182
|
)
|
|
129
183
|
|
|
130
184
|
|
|
@@ -213,12 +267,15 @@ def _woe_bins_unweighted_screen(
|
|
|
213
267
|
config: FeatureScreenConfig,
|
|
214
268
|
*,
|
|
215
269
|
prefit_woe_engine: Any | None = None,
|
|
270
|
+
selection_evidence: Any | None = None,
|
|
216
271
|
) -> FeatureScreenResult:
|
|
217
272
|
from Modeling_Tool import CorrelationFilter, PSICalculator, VarExtractionInsights
|
|
218
273
|
|
|
219
274
|
ins, oos, oot = splits["ins"], splits["oos"], splits["oot"]
|
|
220
275
|
current = list(feature_cols)
|
|
221
276
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, None)]
|
|
277
|
+
dropped_rows: list[dict] = []
|
|
278
|
+
stage_tables: dict[str, pd.DataFrame] = {}
|
|
222
279
|
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
223
280
|
ins,
|
|
224
281
|
current,
|
|
@@ -228,6 +285,7 @@ def _woe_bins_unweighted_screen(
|
|
|
228
285
|
on_empty_stage=config.on_empty_stage,
|
|
229
286
|
)
|
|
230
287
|
binner = _resolve_screening_binner(splits, current, target_col, config, prefit_woe_engine)
|
|
288
|
+
engine_fit_features = list(current)
|
|
231
289
|
|
|
232
290
|
psi_table = pd.DataFrame(columns=["var", "psi_ins_oos", "psi_ins_oot", "psi_max"])
|
|
233
291
|
if config.psi_enabled:
|
|
@@ -290,7 +348,7 @@ def _woe_bins_unweighted_screen(
|
|
|
290
348
|
)
|
|
291
349
|
if iv is not None and not iv.empty:
|
|
292
350
|
iv_table = iv.rename(columns={"iv": "iv_weighted"})[["var", "iv_weighted", "n_bins", "missing_rate"]]
|
|
293
|
-
keep = iv
|
|
351
|
+
keep = _iv_band_keep(iv, "iv", config.iv_threshold, config.iv_upper_threshold, dropped_rows)
|
|
294
352
|
n_before = len(current)
|
|
295
353
|
current = _apply_stage_keep(
|
|
296
354
|
current, keep, "iv", summary_rows,
|
|
@@ -312,7 +370,25 @@ def _woe_bins_unweighted_screen(
|
|
|
312
370
|
current = cf.remove_highly_correlated(current, max_iterations=config.corr_max_iterations)
|
|
313
371
|
summary_rows.append(_summary_row("corr", n_before, len(current), config.corr_threshold, None))
|
|
314
372
|
|
|
373
|
+
from .Screen_Gates import apply_post_corr_gates
|
|
374
|
+
|
|
375
|
+
gate_iv_map = (
|
|
376
|
+
dict(zip(iv_table["var"], iv_table["iv_weighted"])) if not iv_table.empty else {}
|
|
377
|
+
)
|
|
378
|
+
current = apply_post_corr_gates(
|
|
379
|
+
ins, current, config, selection_evidence, gate_iv_map,
|
|
380
|
+
summary_rows, dropped_rows, stage_tables,
|
|
381
|
+
weight_col=None, on_empty_stage=config.on_empty_stage,
|
|
382
|
+
)
|
|
383
|
+
|
|
315
384
|
summary_rows.append(_summary_row("final", len(feature_cols), len(current), None, None))
|
|
385
|
+
engine_obj, engine_meta = _screen_engine_payload(
|
|
386
|
+
binner,
|
|
387
|
+
fit_features=engine_fit_features,
|
|
388
|
+
target_col=target_col,
|
|
389
|
+
weight_col=None,
|
|
390
|
+
summary_rows=summary_rows,
|
|
391
|
+
)
|
|
316
392
|
return FeatureScreenResult(
|
|
317
393
|
selected_features=list(current),
|
|
318
394
|
iv_table=iv_table,
|
|
@@ -321,6 +397,13 @@ def _woe_bins_unweighted_screen(
|
|
|
321
397
|
summary=pd.DataFrame(summary_rows),
|
|
322
398
|
missing_rate_table=missing_rate_table,
|
|
323
399
|
missing_rate_dropped=missing_rate_dropped,
|
|
400
|
+
woe_engine=engine_obj,
|
|
401
|
+
woe_engine_meta=engine_meta,
|
|
402
|
+
dropped_detail=(
|
|
403
|
+
pd.DataFrame(dropped_rows, columns=_DROPPED_DETAIL_COLS)
|
|
404
|
+
if dropped_rows else pd.DataFrame(columns=_DROPPED_DETAIL_COLS)
|
|
405
|
+
),
|
|
406
|
+
stage_tables=stage_tables,
|
|
324
407
|
)
|
|
325
408
|
|
|
326
409
|
|
|
@@ -330,6 +413,52 @@ def _bins_to_numpy(bins: Any) -> np.ndarray:
|
|
|
330
413
|
return np.asarray(bins, dtype=object)
|
|
331
414
|
|
|
332
415
|
|
|
416
|
+
def _screen_engine_payload(
|
|
417
|
+
binner: Any | None,
|
|
418
|
+
*,
|
|
419
|
+
fit_features: list[str],
|
|
420
|
+
target_col: str,
|
|
421
|
+
weight_col: str | None,
|
|
422
|
+
summary_rows: list[dict],
|
|
423
|
+
) -> tuple[Any | None, dict[str, Any]]:
|
|
424
|
+
"""Build the (woe_engine, woe_engine_meta) result payload (G00).
|
|
425
|
+
|
|
426
|
+
Monotone engines attach as-is — they hold only per-feature bin tables.
|
|
427
|
+
WOE_Master engines are never attached (they retain the full training
|
|
428
|
+
frame at ``self.train_data``); their woe_table + metadata travel instead.
|
|
429
|
+
"""
|
|
430
|
+
if binner is None:
|
|
431
|
+
return None, {}
|
|
432
|
+
engine_name = type(binner).__name__
|
|
433
|
+
meta: dict[str, Any] = {
|
|
434
|
+
"engine_name": engine_name,
|
|
435
|
+
"fit_features": list(fit_features),
|
|
436
|
+
"target_col": target_col,
|
|
437
|
+
"weight_col": weight_col,
|
|
438
|
+
"screen_stages": [row.get("stage") for row in summary_rows],
|
|
439
|
+
}
|
|
440
|
+
if engine_name == "WOE_Master":
|
|
441
|
+
from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
meta["woe_table"] = as_woe_engine(binner).get_woe_table(varlist=list(fit_features))
|
|
445
|
+
except Exception as exc:
|
|
446
|
+
meta["woe_table"] = None
|
|
447
|
+
meta["woe_table_error"] = repr(exc)
|
|
448
|
+
meta["engine_attached"] = False
|
|
449
|
+
warnings.warn(
|
|
450
|
+
"screening WOE engine is a WOE_Master, which retains the full "
|
|
451
|
+
"training frame — attaching its woe_table and metadata only. "
|
|
452
|
+
"Use woe_engine='monotone' to get a reusable, serializable "
|
|
453
|
+
"screening engine on the result.",
|
|
454
|
+
UserWarning,
|
|
455
|
+
stacklevel=3,
|
|
456
|
+
)
|
|
457
|
+
return None, meta
|
|
458
|
+
meta["engine_attached"] = True
|
|
459
|
+
return binner, meta
|
|
460
|
+
|
|
461
|
+
|
|
333
462
|
def _weighted_woe_bins_screen(
|
|
334
463
|
splits: dict[str, pd.DataFrame],
|
|
335
464
|
feature_cols: list[str],
|
|
@@ -338,6 +467,7 @@ def _weighted_woe_bins_screen(
|
|
|
338
467
|
config: FeatureScreenConfig,
|
|
339
468
|
*,
|
|
340
469
|
prefit_woe_engine: Any | None = None,
|
|
470
|
+
selection_evidence: Any | None = None,
|
|
341
471
|
) -> FeatureScreenResult:
|
|
342
472
|
from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
|
|
343
473
|
|
|
@@ -346,6 +476,8 @@ def _weighted_woe_bins_screen(
|
|
|
346
476
|
y_ins = ins[target_col].astype(float).to_numpy()
|
|
347
477
|
current = list(feature_cols)
|
|
348
478
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, weight_col)]
|
|
479
|
+
dropped_rows: list[dict] = []
|
|
480
|
+
stage_tables: dict[str, pd.DataFrame] = {}
|
|
349
481
|
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
350
482
|
ins,
|
|
351
483
|
current,
|
|
@@ -369,6 +501,7 @@ def _weighted_woe_bins_screen(
|
|
|
369
501
|
categorical_features=config.categorical_features,
|
|
370
502
|
)
|
|
371
503
|
adapter = as_woe_engine(binner)
|
|
504
|
+
engine_fit_features = list(current)
|
|
372
505
|
|
|
373
506
|
# Assign fitted bins once per split. PSI and IV both consume these labels;
|
|
374
507
|
# doing it here avoids a full WOE transform for every feature and stage.
|
|
@@ -447,7 +580,7 @@ def _weighted_woe_bins_screen(
|
|
|
447
580
|
columns=["var", "iv_weighted", "n_bins", "missing_rate"],
|
|
448
581
|
)
|
|
449
582
|
if not iv_table.empty:
|
|
450
|
-
keep = iv_table
|
|
583
|
+
keep = _iv_band_keep(iv_table, "iv_weighted", config.iv_threshold, config.iv_upper_threshold, dropped_rows)
|
|
451
584
|
n_before = len(current)
|
|
452
585
|
current = _apply_stage_keep(
|
|
453
586
|
current, keep, "iv", summary_rows,
|
|
@@ -479,7 +612,25 @@ def _weighted_woe_bins_screen(
|
|
|
479
612
|
)
|
|
480
613
|
summary_rows.append(_summary_row("corr", n_before, len(current), config.corr_threshold, weight_col))
|
|
481
614
|
|
|
615
|
+
from .Screen_Gates import apply_post_corr_gates
|
|
616
|
+
|
|
617
|
+
gate_iv_map = (
|
|
618
|
+
dict(zip(iv_table["var"], iv_table["iv_weighted"])) if not iv_table.empty else {}
|
|
619
|
+
)
|
|
620
|
+
current = apply_post_corr_gates(
|
|
621
|
+
ins, current, config, selection_evidence, gate_iv_map,
|
|
622
|
+
summary_rows, dropped_rows, stage_tables,
|
|
623
|
+
weight_col=weight_col, on_empty_stage=config.on_empty_stage,
|
|
624
|
+
)
|
|
625
|
+
|
|
482
626
|
summary_rows.append(_summary_row("final", len(feature_cols), len(current), None, weight_col))
|
|
627
|
+
engine_obj, engine_meta = _screen_engine_payload(
|
|
628
|
+
binner,
|
|
629
|
+
fit_features=engine_fit_features,
|
|
630
|
+
target_col=target_col,
|
|
631
|
+
weight_col=weight_col,
|
|
632
|
+
summary_rows=summary_rows,
|
|
633
|
+
)
|
|
483
634
|
return FeatureScreenResult(
|
|
484
635
|
selected_features=list(current),
|
|
485
636
|
iv_table=iv_table,
|
|
@@ -488,6 +639,13 @@ def _weighted_woe_bins_screen(
|
|
|
488
639
|
summary=pd.DataFrame(summary_rows),
|
|
489
640
|
missing_rate_table=missing_rate_table,
|
|
490
641
|
missing_rate_dropped=missing_rate_dropped,
|
|
642
|
+
woe_engine=engine_obj,
|
|
643
|
+
woe_engine_meta=engine_meta,
|
|
644
|
+
dropped_detail=(
|
|
645
|
+
pd.DataFrame(dropped_rows, columns=_DROPPED_DETAIL_COLS)
|
|
646
|
+
if dropped_rows else pd.DataFrame(columns=_DROPPED_DETAIL_COLS)
|
|
647
|
+
),
|
|
648
|
+
stage_tables=stage_tables,
|
|
491
649
|
)
|
|
492
650
|
|
|
493
651
|
|
|
@@ -499,6 +657,7 @@ def feature_screen(
|
|
|
499
657
|
weight_col: str | None = None,
|
|
500
658
|
config: FeatureScreenConfig | None = None,
|
|
501
659
|
prefit_woe_engine: Any | None = None,
|
|
660
|
+
selection_evidence: Any | None = None,
|
|
502
661
|
) -> FeatureScreenResult:
|
|
503
662
|
"""Run PSI -> IV -> correlation screening on pre-split INS/OOS/OOT frames."""
|
|
504
663
|
cfg = config or FeatureScreenConfig()
|
|
@@ -513,6 +672,7 @@ def feature_screen(
|
|
|
513
672
|
weight_col,
|
|
514
673
|
cfg,
|
|
515
674
|
prefit_woe_engine=prefit_woe_engine,
|
|
675
|
+
selection_evidence=selection_evidence,
|
|
516
676
|
)
|
|
517
677
|
return _weighted_screen_impl(
|
|
518
678
|
splits,
|
|
@@ -524,6 +684,7 @@ def feature_screen(
|
|
|
524
684
|
psi_compare_splits=list(cfg.psi_compare_splits),
|
|
525
685
|
iv_enabled=cfg.iv_enabled,
|
|
526
686
|
iv_threshold=cfg.iv_threshold,
|
|
687
|
+
iv_upper_threshold=cfg.iv_upper_threshold,
|
|
527
688
|
iv_bins=cfg.iv_bins,
|
|
528
689
|
min_bin_prop=cfg.min_bin_prop,
|
|
529
690
|
corr_enabled=cfg.corr_enabled,
|
|
@@ -538,6 +699,8 @@ def feature_screen(
|
|
|
538
699
|
prefit_woe_engine=prefit_woe_engine,
|
|
539
700
|
missing_rate_threshold=cfg.missing_rate_threshold,
|
|
540
701
|
missing_rate_ref=cfg.missing_rate_ref,
|
|
702
|
+
gates_config=cfg,
|
|
703
|
+
selection_evidence=selection_evidence,
|
|
541
704
|
)
|
|
542
705
|
|
|
543
706
|
if use_woe_bins or prefit_woe_engine is not None:
|
|
@@ -547,6 +710,7 @@ def feature_screen(
|
|
|
547
710
|
target_col,
|
|
548
711
|
cfg,
|
|
549
712
|
prefit_woe_engine=prefit_woe_engine,
|
|
713
|
+
selection_evidence=selection_evidence,
|
|
550
714
|
)
|
|
551
715
|
|
|
552
716
|
return _legacy_unweighted_screen(
|
|
@@ -558,6 +722,7 @@ def feature_screen(
|
|
|
558
722
|
psi_compare_splits=list(cfg.psi_compare_splits),
|
|
559
723
|
iv_enabled=cfg.iv_enabled,
|
|
560
724
|
iv_threshold=cfg.iv_threshold,
|
|
725
|
+
iv_upper_threshold=cfg.iv_upper_threshold,
|
|
561
726
|
iv_bins=cfg.iv_bins,
|
|
562
727
|
iv_min_bin_prop=cfg.iv_min_bin_prop,
|
|
563
728
|
corr_enabled=cfg.corr_enabled,
|
|
@@ -570,6 +735,8 @@ def feature_screen(
|
|
|
570
735
|
on_empty_stage=cfg.on_empty_stage,
|
|
571
736
|
missing_rate_threshold=cfg.missing_rate_threshold,
|
|
572
737
|
missing_rate_ref=cfg.missing_rate_ref,
|
|
738
|
+
gates_config=cfg,
|
|
739
|
+
selection_evidence=selection_evidence,
|
|
573
740
|
)
|
|
574
741
|
|
|
575
742
|
|