SuperModelingFactory 0.3.13__tar.gz → 0.3.15__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.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Distribution_Tool.py +34 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Distribution_Tool.pyi +1 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Feature_Insights.py +6 -5
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Feature_Screen.py +67 -8
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +3 -3
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Weighted_Screen.py +314 -18
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/feature_validation.py +17 -3
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/screening_artifact.py +4 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/__init__.py +1 -1
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/PKG-INFO +1 -1
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/SuperModelingFactory.egg-info/PKG-INFO +1 -1
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/pyproject.toml +1 -1
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/setup.py +1 -1
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/ExcelMaster/ExcelFormatTool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/ExcelMaster/ExcelMaster.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/ExcelMaster/Template.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/ExcelMaster/Utility.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/ExcelMaster/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/LICENSE +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/MANIFEST.in +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Binning_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Proc_Compare.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Slope_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/kDataFrame.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/utils.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/evaluate_model.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Explainability/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/Backward_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/GBM_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/LRM_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/_common.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/credit_model.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Sample_Split.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/UAT/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Master.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/simsun.ttc +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/README.md +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Report/Report_Tool.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Report/__init__.py +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/SuperModelingFactory.egg-info/SOURCES.txt +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/SuperModelingFactory.egg-info/requires.txt +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/SuperModelingFactory.egg-info/top_level.txt +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/requirements.txt +0 -0
- {supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/setup.cfg +0 -0
|
@@ -186,6 +186,40 @@ def proc_means_by_grp(data, varlist, groupby=None, spec_missing_value=None, q=No
|
|
|
186
186
|
return means_rpt
|
|
187
187
|
|
|
188
188
|
|
|
189
|
+
_SCREENING_MEANS_COLS = ["attribute", "N_ALL", "N", "MISSING_RATE", "MIN", "MEAN", "MAX"]
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def proc_means_for_screening(data, varlist, spec_missing_value=None, q=None):
|
|
193
|
+
"""Build a uniform means table for IV screening reports.
|
|
194
|
+
|
|
195
|
+
``proc_means_by_grp`` returns object stats (unique/top/freq) when numeric and
|
|
196
|
+
categorical columns are melted together. This helper splits by dtype so MIN,
|
|
197
|
+
MEAN, and MAX are always present (NaN for non-numeric features).
|
|
198
|
+
"""
|
|
199
|
+
if not varlist:
|
|
200
|
+
return pd.DataFrame(columns=_SCREENING_MEANS_COLS)
|
|
201
|
+
|
|
202
|
+
numeric = [v for v in varlist if v in data.columns and pd.api.types.is_numeric_dtype(data[v])]
|
|
203
|
+
other = [v for v in varlist if v not in numeric and v in data.columns]
|
|
204
|
+
frames = []
|
|
205
|
+
|
|
206
|
+
if numeric:
|
|
207
|
+
frames.append(proc_means_by_grp(data, numeric, spec_missing_value=spec_missing_value, q=q))
|
|
208
|
+
|
|
209
|
+
for var in other:
|
|
210
|
+
part = proc_means_by_grp(data, [var], spec_missing_value=spec_missing_value, q=q)
|
|
211
|
+
for col in ("MIN", "MEAN", "MAX"):
|
|
212
|
+
if col not in part.columns:
|
|
213
|
+
part[col] = np.nan
|
|
214
|
+
frames.append(part)
|
|
215
|
+
|
|
216
|
+
if not frames:
|
|
217
|
+
return pd.DataFrame(columns=_SCREENING_MEANS_COLS)
|
|
218
|
+
|
|
219
|
+
out = pd.concat(frames, ignore_index=True)
|
|
220
|
+
return out[_SCREENING_MEANS_COLS]
|
|
221
|
+
|
|
222
|
+
|
|
189
223
|
class DistributionShiftAnalyzer:
|
|
190
224
|
"""分布偏移分析器。
|
|
191
225
|
|
|
@@ -25,6 +25,7 @@ class proc_means:
|
|
|
25
25
|
def group_sum(self): ...
|
|
26
26
|
def __call__(self, q = None): ...
|
|
27
27
|
def proc_means_by_grp(data, varlist, groupby = None, spec_missing_value = None, q = None): ...
|
|
28
|
+
def proc_means_for_screening(data, varlist, spec_missing_value = None, q = None): ...
|
|
28
29
|
|
|
29
30
|
class DistributionShiftAnalyzer:
|
|
30
31
|
def __init__(self, data, grp_name, benchmark_value): ...
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
from tqdm import tqdm
|
|
8
8
|
|
|
9
|
-
from .Distribution_Tool import proc_means_by_grp
|
|
9
|
+
from .Distribution_Tool import proc_means_by_grp, proc_means_for_screening
|
|
10
10
|
import logging
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
@@ -208,14 +208,15 @@ class VarExtractionInsights:
|
|
|
208
208
|
high_iv_summary = iv_info_res.query(f"IV >= {iv_cut}").round(4)
|
|
209
209
|
high_iv_varlist = high_iv_summary['var'].tolist()
|
|
210
210
|
|
|
211
|
-
means = proc_means_by_grp(data, high_iv_varlist, spec_missing_value=self.missing_rate_ref)
|
|
212
|
-
|
|
213
211
|
if len(high_iv_varlist) == 0:
|
|
214
212
|
logger.info(f"WARNING: No variable with IV >= {iv_cut}")
|
|
215
|
-
|
|
213
|
+
|
|
214
|
+
means = proc_means_for_screening(
|
|
215
|
+
data, high_iv_varlist, spec_missing_value=self.missing_rate_ref,
|
|
216
|
+
)
|
|
216
217
|
|
|
217
218
|
fnl_summary = high_iv_summary.merge(
|
|
218
|
-
means
|
|
219
|
+
means,
|
|
219
220
|
left_on=['var'],
|
|
220
221
|
right_on=['attribute'],
|
|
221
222
|
how='left'
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Feature_Screen.py
RENAMED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any, Mapping
|
|
7
|
+
from typing import Any, Literal, Mapping
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
import pandas as pd
|
|
@@ -13,14 +13,16 @@ from Modeling_Tool.Core.sample_weight_utils import resolve_sample_weight
|
|
|
13
13
|
|
|
14
14
|
from .Weighted_Screen import (
|
|
15
15
|
WeightedScreenResult,
|
|
16
|
+
_apply_missing_rate_stage,
|
|
17
|
+
_apply_stage_keep,
|
|
16
18
|
_corr_dedup_weighted,
|
|
17
19
|
_legacy_unweighted_screen,
|
|
18
20
|
_psi_from_distributions,
|
|
19
21
|
_resolve_splits,
|
|
20
22
|
_summary_row,
|
|
21
23
|
_weighted_bin_distribution,
|
|
24
|
+
_weighted_corr_for_screen,
|
|
22
25
|
_weighted_iv_from_assigned_bins,
|
|
23
|
-
_weighted_pearson_corr_matrix,
|
|
24
26
|
_weighted_screen_impl,
|
|
25
27
|
)
|
|
26
28
|
|
|
@@ -55,6 +57,10 @@ class FeatureScreenConfig:
|
|
|
55
57
|
corr_threshold: float = 0.75
|
|
56
58
|
corr_max_iterations: int = 10
|
|
57
59
|
corr_use_woe_bins: bool = False
|
|
60
|
+
corr_nan_policy: Literal["pairwise", "median_fill", "raise"] = "pairwise"
|
|
61
|
+
on_empty_stage: Literal["keep_all_warn", "raise"] = "keep_all_warn"
|
|
62
|
+
missing_rate_threshold: float | None = None
|
|
63
|
+
missing_rate_ref: float | int = -999999
|
|
58
64
|
woe_engine: str = "equal_freq"
|
|
59
65
|
woe_fit_query: str | None = None
|
|
60
66
|
woe_params: dict[str, Any] = field(
|
|
@@ -100,6 +106,10 @@ def screen_config_from_mapping(
|
|
|
100
106
|
corr_threshold=float(cfg.get("corr_threshold", 0.75)),
|
|
101
107
|
corr_max_iterations=int(cfg.get("corr_max_iterations", 10)),
|
|
102
108
|
corr_use_woe_bins=bool(cfg.get("corr_use_woe_bins", False)),
|
|
109
|
+
corr_nan_policy=str(cfg.get("corr_nan_policy", "pairwise")), # type: ignore[arg-type]
|
|
110
|
+
on_empty_stage=str(cfg.get("on_empty_stage", "keep_all_warn")), # type: ignore[arg-type]
|
|
111
|
+
missing_rate_threshold=cfg.get("missing_rate_threshold"),
|
|
112
|
+
missing_rate_ref=cfg.get("missing_rate_ref", -999999),
|
|
103
113
|
woe_engine=str(woe_engine or cfg.get("woe_engine", "equal_freq")),
|
|
104
114
|
woe_fit_query=woe_fit_query if woe_fit_query is not None else cfg.get("woe_fit_query"),
|
|
105
115
|
woe_params=dict(FeatureScreenConfig().woe_params | dict(woe_params or cfg.get("woe_params") or {})),
|
|
@@ -206,6 +216,14 @@ def _woe_bins_unweighted_screen(
|
|
|
206
216
|
ins, oos, oot = splits["ins"], splits["oos"], splits["oot"]
|
|
207
217
|
current = list(feature_cols)
|
|
208
218
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, None)]
|
|
219
|
+
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
220
|
+
ins,
|
|
221
|
+
current,
|
|
222
|
+
summary_rows,
|
|
223
|
+
missing_rate_threshold=config.missing_rate_threshold,
|
|
224
|
+
missing_rate_ref=config.missing_rate_ref,
|
|
225
|
+
on_empty_stage=config.on_empty_stage,
|
|
226
|
+
)
|
|
209
227
|
binner = _resolve_screening_binner(splits, feature_cols, target_col, config, prefit_woe_engine)
|
|
210
228
|
|
|
211
229
|
psi_table = pd.DataFrame(columns=["var", "psi_ins_oos", "psi_ins_oot", "psi_max"])
|
|
@@ -236,7 +254,10 @@ def _woe_bins_unweighted_screen(
|
|
|
236
254
|
psi_table["psi_max"] = psi_table[compare_cols].max(axis=1, skipna=True)
|
|
237
255
|
keep = psi_table.loc[psi_table["psi_max"] < config.psi_threshold, "var"].tolist()
|
|
238
256
|
n_before = len(current)
|
|
239
|
-
current =
|
|
257
|
+
current = _apply_stage_keep(
|
|
258
|
+
current, keep, "psi", summary_rows,
|
|
259
|
+
on_empty_stage=config.on_empty_stage, threshold=config.psi_threshold,
|
|
260
|
+
)
|
|
240
261
|
summary_rows.append(_summary_row("psi", n_before, len(current), config.psi_threshold, None))
|
|
241
262
|
|
|
242
263
|
iv_table = pd.DataFrame(columns=["var", "iv_weighted", "n_bins", "missing_rate"])
|
|
@@ -268,7 +289,10 @@ def _woe_bins_unweighted_screen(
|
|
|
268
289
|
iv_table = iv.rename(columns={"iv": "iv_weighted"})[["var", "iv_weighted", "n_bins", "missing_rate"]]
|
|
269
290
|
keep = iv.loc[iv["iv"] >= config.iv_threshold, "var"].tolist()
|
|
270
291
|
n_before = len(current)
|
|
271
|
-
current =
|
|
292
|
+
current = _apply_stage_keep(
|
|
293
|
+
current, keep, "iv", summary_rows,
|
|
294
|
+
on_empty_stage=config.on_empty_stage, threshold=config.iv_threshold,
|
|
295
|
+
)
|
|
272
296
|
summary_rows.append(_summary_row("iv", n_before, len(current), config.iv_threshold, None))
|
|
273
297
|
|
|
274
298
|
corr_dropped = pd.DataFrame(columns=["var_a", "var_b", "corr", "iv_a", "iv_b", "kept", "dropped"])
|
|
@@ -292,6 +316,8 @@ def _woe_bins_unweighted_screen(
|
|
|
292
316
|
psi_table=psi_table,
|
|
293
317
|
corr_dropped=corr_dropped,
|
|
294
318
|
summary=pd.DataFrame(summary_rows),
|
|
319
|
+
missing_rate_table=missing_rate_table,
|
|
320
|
+
missing_rate_dropped=missing_rate_dropped,
|
|
295
321
|
)
|
|
296
322
|
|
|
297
323
|
|
|
@@ -317,6 +343,15 @@ def _weighted_woe_bins_screen(
|
|
|
317
343
|
y_ins = ins[target_col].astype(float).to_numpy()
|
|
318
344
|
current = list(feature_cols)
|
|
319
345
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, weight_col)]
|
|
346
|
+
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
347
|
+
ins,
|
|
348
|
+
current,
|
|
349
|
+
summary_rows,
|
|
350
|
+
missing_rate_threshold=config.missing_rate_threshold,
|
|
351
|
+
missing_rate_ref=config.missing_rate_ref,
|
|
352
|
+
weight_col=weight_col,
|
|
353
|
+
on_empty_stage=config.on_empty_stage,
|
|
354
|
+
)
|
|
320
355
|
|
|
321
356
|
binner = prefit_woe_engine
|
|
322
357
|
if binner is None:
|
|
@@ -360,7 +395,11 @@ def _weighted_woe_bins_screen(
|
|
|
360
395
|
psi_table["psi_max"] = psi_table[compare_cols].max(axis=1, skipna=True)
|
|
361
396
|
keep = psi_table.loc[psi_table["psi_max"] < config.psi_threshold, "var"].tolist()
|
|
362
397
|
n_before = len(current)
|
|
363
|
-
current =
|
|
398
|
+
current = _apply_stage_keep(
|
|
399
|
+
current, keep, "psi", summary_rows,
|
|
400
|
+
on_empty_stage=config.on_empty_stage, weight_col=weight_col,
|
|
401
|
+
threshold=config.psi_threshold, intersect=True,
|
|
402
|
+
)
|
|
364
403
|
summary_rows.append(_summary_row("psi", n_before, len(current), config.psi_threshold, weight_col))
|
|
365
404
|
else:
|
|
366
405
|
psi_table["psi_max"] = pd.Series(dtype=float)
|
|
@@ -387,15 +426,24 @@ def _weighted_woe_bins_screen(
|
|
|
387
426
|
if not iv_table.empty:
|
|
388
427
|
keep = iv_table.loc[iv_table["iv_weighted"] >= config.iv_threshold, "var"].tolist()
|
|
389
428
|
n_before = len(current)
|
|
390
|
-
current =
|
|
429
|
+
current = _apply_stage_keep(
|
|
430
|
+
current, keep, "iv", summary_rows,
|
|
431
|
+
on_empty_stage=config.on_empty_stage, weight_col=weight_col,
|
|
432
|
+
threshold=config.iv_threshold, intersect=True,
|
|
433
|
+
)
|
|
391
434
|
summary_rows.append(_summary_row("iv", n_before, len(current), config.iv_threshold, weight_col))
|
|
392
435
|
else:
|
|
393
436
|
iv_table = pd.DataFrame(columns=["var", "iv_weighted", "n_bins", "missing_rate"])
|
|
394
437
|
|
|
395
438
|
corr_dropped = pd.DataFrame(columns=["var_a", "var_b", "corr", "iv_a", "iv_b", "kept", "dropped"])
|
|
396
439
|
if config.corr_enabled and len(current) > 1:
|
|
397
|
-
|
|
398
|
-
|
|
440
|
+
corr = _weighted_corr_for_screen(
|
|
441
|
+
ins, current, w_ins,
|
|
442
|
+
corr_use_woe_bins=config.corr_use_woe_bins,
|
|
443
|
+
corr_nan_policy=config.corr_nan_policy,
|
|
444
|
+
adapter=adapter,
|
|
445
|
+
binner=binner,
|
|
446
|
+
)
|
|
399
447
|
iv_map = dict(zip(iv_table["var"], iv_table["iv_weighted"])) if not iv_table.empty else {}
|
|
400
448
|
n_before = len(current)
|
|
401
449
|
current, corr_dropped = _corr_dedup_weighted(
|
|
@@ -414,6 +462,8 @@ def _weighted_woe_bins_screen(
|
|
|
414
462
|
psi_table=psi_table,
|
|
415
463
|
corr_dropped=corr_dropped,
|
|
416
464
|
summary=pd.DataFrame(summary_rows),
|
|
465
|
+
missing_rate_table=missing_rate_table,
|
|
466
|
+
missing_rate_dropped=missing_rate_dropped,
|
|
417
467
|
)
|
|
418
468
|
|
|
419
469
|
|
|
@@ -457,6 +507,12 @@ def feature_screen(
|
|
|
457
507
|
corr_max_iterations=cfg.corr_max_iterations,
|
|
458
508
|
content=cfg.content,
|
|
459
509
|
precision=cfg.precision,
|
|
510
|
+
corr_use_woe_bins=cfg.corr_use_woe_bins,
|
|
511
|
+
corr_nan_policy=cfg.corr_nan_policy,
|
|
512
|
+
on_empty_stage=cfg.on_empty_stage,
|
|
513
|
+
prefit_woe_engine=prefit_woe_engine,
|
|
514
|
+
missing_rate_threshold=cfg.missing_rate_threshold,
|
|
515
|
+
missing_rate_ref=cfg.missing_rate_ref,
|
|
460
516
|
)
|
|
461
517
|
|
|
462
518
|
if use_woe_bins or prefit_woe_engine is not None:
|
|
@@ -486,6 +542,9 @@ def feature_screen(
|
|
|
486
542
|
plot_path=cfg.plot_path,
|
|
487
543
|
plot_outputs=cfg.plot_outputs,
|
|
488
544
|
iv_equal_freq=cfg.iv_equal_freq,
|
|
545
|
+
on_empty_stage=cfg.on_empty_stage,
|
|
546
|
+
missing_rate_threshold=cfg.missing_rate_threshold,
|
|
547
|
+
missing_rate_ref=cfg.missing_rate_ref,
|
|
489
548
|
)
|
|
490
549
|
|
|
491
550
|
|
|
@@ -19,7 +19,7 @@ from .Feature_Insights import (
|
|
|
19
19
|
VarExtractionInsights as _BaseVarExtractionInsights,
|
|
20
20
|
var_corr_filter,
|
|
21
21
|
)
|
|
22
|
-
from .Distribution_Tool import
|
|
22
|
+
from .Distribution_Tool import proc_means_for_screening
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def _psi_from_bins(expected_bins: pd.Series, current_bins: pd.Series, content: float) -> float:
|
|
@@ -110,9 +110,9 @@ def _screening_summary_from_engine(
|
|
|
110
110
|
high_iv_summary = pd.DataFrame(rows).query(f"iv >= {iv_cut}").round(4)
|
|
111
111
|
high_iv_varlist = high_iv_summary["var"].tolist()
|
|
112
112
|
if high_iv_varlist:
|
|
113
|
-
means =
|
|
113
|
+
means = proc_means_for_screening(data, high_iv_varlist, spec_missing_value=missing_rate_ref)
|
|
114
114
|
summary = high_iv_summary.merge(
|
|
115
|
-
means
|
|
115
|
+
means,
|
|
116
116
|
left_on="var",
|
|
117
117
|
right_on="attribute",
|
|
118
118
|
how="left",
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/Weighted_Screen.py
RENAMED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import warnings
|
|
6
|
+
from dataclasses import dataclass, field
|
|
6
7
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
|
+
from typing import Any, Literal
|
|
8
9
|
|
|
9
10
|
import numpy as np
|
|
10
11
|
import pandas as pd
|
|
@@ -15,6 +16,9 @@ from Modeling_Tool.Core.utils import calc_iv
|
|
|
15
16
|
_MISSING_BIN = "__MISSING__"
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
_MISSING_RATE_COLS = ["var", "missing_rate"]
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
@dataclass
|
|
19
23
|
class WeightedScreenResult:
|
|
20
24
|
selected_features: list[str]
|
|
@@ -22,6 +26,12 @@ class WeightedScreenResult:
|
|
|
22
26
|
psi_table: pd.DataFrame
|
|
23
27
|
corr_dropped: pd.DataFrame
|
|
24
28
|
summary: pd.DataFrame
|
|
29
|
+
missing_rate_table: pd.DataFrame = field(
|
|
30
|
+
default_factory=lambda: pd.DataFrame(columns=_MISSING_RATE_COLS),
|
|
31
|
+
)
|
|
32
|
+
missing_rate_dropped: pd.DataFrame = field(
|
|
33
|
+
default_factory=lambda: pd.DataFrame(columns=_MISSING_RATE_COLS),
|
|
34
|
+
)
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
def _summary_row(stage: str, n_in: int, n_out: int, threshold: Any, weight_col: str | None) -> dict:
|
|
@@ -185,21 +195,255 @@ def _weighted_iv_for_var(
|
|
|
185
195
|
return iv, len(rows), missing_rate
|
|
186
196
|
|
|
187
197
|
|
|
188
|
-
def
|
|
198
|
+
def _weighted_median(x: np.ndarray, w: np.ndarray) -> float:
|
|
199
|
+
mask = np.isfinite(x)
|
|
200
|
+
if not mask.any():
|
|
201
|
+
return np.nan
|
|
202
|
+
xv = x[mask].astype(float)
|
|
203
|
+
wv = w[mask].astype(float)
|
|
204
|
+
order = np.argsort(xv)
|
|
205
|
+
xv = xv[order]
|
|
206
|
+
wv = wv[order]
|
|
207
|
+
cumw = np.cumsum(wv)
|
|
208
|
+
total = float(cumw[-1])
|
|
209
|
+
if total <= 0:
|
|
210
|
+
return float(np.median(xv))
|
|
211
|
+
idx = min(int(np.searchsorted(cumw, total / 2.0, side="right")), len(xv) - 1)
|
|
212
|
+
return float(xv[idx])
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _weighted_corr_pair(xi: np.ndarray, xj: np.ndarray, w: np.ndarray) -> float:
|
|
216
|
+
mask = np.isfinite(xi) & np.isfinite(xj)
|
|
217
|
+
if mask.sum() < 2:
|
|
218
|
+
return 0.0
|
|
219
|
+
wi = w[mask].astype(float)
|
|
220
|
+
xi = xi[mask].astype(float)
|
|
221
|
+
xj = xj[mask].astype(float)
|
|
222
|
+
w_sum = wi.sum()
|
|
223
|
+
if w_sum <= 0:
|
|
224
|
+
c = np.corrcoef(xi, xj)
|
|
225
|
+
return float(c[0, 1]) if np.isfinite(c[0, 1]) else 0.0
|
|
226
|
+
w_norm = wi / w_sum
|
|
227
|
+
mi = np.average(xi, weights=w_norm)
|
|
228
|
+
mj = np.average(xj, weights=w_norm)
|
|
229
|
+
di = xi - mi
|
|
230
|
+
dj = xj - mj
|
|
231
|
+
cov = np.average(di * dj, weights=w_norm)
|
|
232
|
+
si = np.sqrt(np.average(di * di, weights=w_norm))
|
|
233
|
+
sj = np.sqrt(np.average(dj * dj, weights=w_norm))
|
|
234
|
+
if si == 0 or sj == 0:
|
|
235
|
+
return 0.0
|
|
236
|
+
return float(cov / (si * sj))
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _weighted_pearson_corr_matrix(
|
|
240
|
+
X: np.ndarray,
|
|
241
|
+
w: np.ndarray,
|
|
242
|
+
*,
|
|
243
|
+
nan_policy: Literal["pairwise", "median_fill", "raise"] = "pairwise",
|
|
244
|
+
) -> np.ndarray:
|
|
189
245
|
if X.shape[1] == 0:
|
|
190
246
|
return np.empty((0, 0))
|
|
191
247
|
w = np.asarray(w, dtype=float)
|
|
248
|
+
k = X.shape[1]
|
|
249
|
+
|
|
250
|
+
if nan_policy == "raise" and np.any(~np.isfinite(X)):
|
|
251
|
+
raise ValueError(
|
|
252
|
+
"weighted correlation input contains NaN; set corr_nan_policy to "
|
|
253
|
+
"'pairwise' or 'median_fill' to proceed"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
nan_mask = ~np.isfinite(X)
|
|
257
|
+
nan_fraction = float(nan_mask.sum()) / float(X.size) if X.size else 0.0
|
|
258
|
+
|
|
259
|
+
if nan_policy == "pairwise":
|
|
260
|
+
corr = np.eye(k, dtype=float)
|
|
261
|
+
insufficient_pairs = 0
|
|
262
|
+
total_pairs = k * (k - 1) // 2
|
|
263
|
+
for i in range(k):
|
|
264
|
+
for j in range(i + 1, k):
|
|
265
|
+
c = _weighted_corr_pair(X[:, i], X[:, j], w)
|
|
266
|
+
corr[i, j] = c
|
|
267
|
+
corr[j, i] = c
|
|
268
|
+
overlap = int((np.isfinite(X[:, i]) & np.isfinite(X[:, j])).sum())
|
|
269
|
+
if overlap < 2:
|
|
270
|
+
insufficient_pairs += 1
|
|
271
|
+
if total_pairs > 0 and insufficient_pairs / total_pairs > 0.01:
|
|
272
|
+
warnings.warn(
|
|
273
|
+
f"[feature_screen] weighted corr: >1% of feature pairs have insufficient "
|
|
274
|
+
f"overlapping observations ({insufficient_pairs}/{total_pairs})",
|
|
275
|
+
stacklevel=3,
|
|
276
|
+
)
|
|
277
|
+
return corr
|
|
278
|
+
|
|
279
|
+
X_use = X.astype(float).copy()
|
|
280
|
+
if nan_policy == "median_fill":
|
|
281
|
+
for j in range(k):
|
|
282
|
+
col = X_use[:, j]
|
|
283
|
+
bad = ~np.isfinite(col)
|
|
284
|
+
if bad.any():
|
|
285
|
+
fill = _weighted_median(col, w)
|
|
286
|
+
if np.isfinite(fill):
|
|
287
|
+
col[bad] = fill
|
|
288
|
+
X_use[:, j] = col
|
|
289
|
+
|
|
192
290
|
w_sum = w.sum()
|
|
193
291
|
if w_sum <= 0:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
292
|
+
out = np.corrcoef(X_use, rowvar=False)
|
|
293
|
+
out = np.nan_to_num(out, nan=0.0)
|
|
294
|
+
else:
|
|
295
|
+
w_norm = w / w_sum
|
|
296
|
+
mean = np.average(X_use, axis=0, weights=w_norm)
|
|
297
|
+
Xc = X_use - mean
|
|
298
|
+
cov = np.cov(Xc, rowvar=False, aweights=w, ddof=0)
|
|
299
|
+
std = np.sqrt(np.diag(cov))
|
|
300
|
+
std[std == 0] = np.nan
|
|
301
|
+
out = cov / np.outer(std, std)
|
|
302
|
+
out = np.nan_to_num(out, nan=0.0)
|
|
303
|
+
|
|
304
|
+
if nan_fraction > 0.01:
|
|
305
|
+
warnings.warn(
|
|
306
|
+
f"[feature_screen] weighted corr: {nan_fraction:.1%} of matrix entries were NaN "
|
|
307
|
+
f"before computation (policy={nan_policy!r})",
|
|
308
|
+
stacklevel=3,
|
|
309
|
+
)
|
|
310
|
+
return out
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _weighted_corr_for_screen(
|
|
314
|
+
ins: pd.DataFrame,
|
|
315
|
+
current: list[str],
|
|
316
|
+
w_ins: np.ndarray,
|
|
317
|
+
*,
|
|
318
|
+
corr_use_woe_bins: bool,
|
|
319
|
+
corr_nan_policy: Literal["pairwise", "median_fill", "raise"],
|
|
320
|
+
adapter: Any | None = None,
|
|
321
|
+
binner: Any | None = None,
|
|
322
|
+
) -> np.ndarray:
|
|
323
|
+
"""Build weighted correlation matrix for screening (WOE or raw-value path)."""
|
|
324
|
+
from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
|
|
325
|
+
|
|
326
|
+
if corr_use_woe_bins:
|
|
327
|
+
eng = adapter if adapter is not None else (as_woe_engine(binner) if binner is not None else None)
|
|
328
|
+
if eng is not None:
|
|
329
|
+
suffix = getattr(eng, "woe_suffix", "_woe")
|
|
330
|
+
woe_ins = eng.transform(ins, varlist=current)
|
|
331
|
+
cols = [f"{v}{suffix}" for v in current if f"{v}{suffix}" in woe_ins.columns]
|
|
332
|
+
if cols:
|
|
333
|
+
X = woe_ins[cols].to_numpy(dtype=float)
|
|
334
|
+
return _weighted_pearson_corr_matrix(X, w_ins, nan_policy="pairwise")
|
|
335
|
+
X = ins[current].astype(float).to_numpy()
|
|
336
|
+
return _weighted_pearson_corr_matrix(X, w_ins, nan_policy=corr_nan_policy)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _missing_rate_for_series(
|
|
340
|
+
series: pd.Series,
|
|
341
|
+
*,
|
|
342
|
+
w: np.ndarray | None = None,
|
|
343
|
+
missing_rate_ref: Any = None,
|
|
344
|
+
) -> float:
|
|
345
|
+
values = series.copy()
|
|
346
|
+
if missing_rate_ref is not None:
|
|
347
|
+
values = values.replace(missing_rate_ref, np.nan)
|
|
348
|
+
if w is None:
|
|
349
|
+
total = len(values)
|
|
350
|
+
if total == 0:
|
|
351
|
+
return 1.0
|
|
352
|
+
valid = int(values.notna().sum())
|
|
353
|
+
return 1.0 - float(valid) / float(total)
|
|
354
|
+
arr = values.to_numpy(dtype=float, copy=False)
|
|
355
|
+
finite = np.isfinite(arr) & values.notna().to_numpy()
|
|
356
|
+
total_w = float(np.sum(w))
|
|
357
|
+
if total_w <= 0:
|
|
358
|
+
return 1.0
|
|
359
|
+
return 1.0 - float(np.sum(w[finite])) / total_w
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _apply_missing_rate_stage(
|
|
363
|
+
ins: pd.DataFrame,
|
|
364
|
+
current: list[str],
|
|
365
|
+
summary_rows: list[dict],
|
|
366
|
+
*,
|
|
367
|
+
missing_rate_threshold: float | None,
|
|
368
|
+
missing_rate_ref: Any = None,
|
|
369
|
+
weight_col: str | None = None,
|
|
370
|
+
on_empty_stage: Literal["keep_all_warn", "raise"] = "keep_all_warn",
|
|
371
|
+
) -> tuple[list[str], pd.DataFrame, pd.DataFrame]:
|
|
372
|
+
empty = pd.DataFrame(columns=_MISSING_RATE_COLS)
|
|
373
|
+
if missing_rate_threshold is None:
|
|
374
|
+
return current, empty.copy(), empty.copy()
|
|
375
|
+
|
|
376
|
+
w = (
|
|
377
|
+
resolve_sample_weight(data=ins, weight_col=weight_col, expected_len=len(ins))
|
|
378
|
+
if weight_col is not None
|
|
379
|
+
else None
|
|
380
|
+
)
|
|
381
|
+
records: list[dict] = []
|
|
382
|
+
for var in current:
|
|
383
|
+
if var not in ins.columns:
|
|
384
|
+
continue
|
|
385
|
+
records.append({
|
|
386
|
+
"var": var,
|
|
387
|
+
"missing_rate": _missing_rate_for_series(
|
|
388
|
+
ins[var], w=w, missing_rate_ref=missing_rate_ref,
|
|
389
|
+
),
|
|
390
|
+
})
|
|
391
|
+
missing_rate_table = pd.DataFrame(records) if records else empty.copy()
|
|
392
|
+
keep = missing_rate_table.loc[
|
|
393
|
+
missing_rate_table["missing_rate"] <= missing_rate_threshold, "var"
|
|
394
|
+
].tolist()
|
|
395
|
+
missing_rate_dropped = missing_rate_table.loc[
|
|
396
|
+
missing_rate_table["missing_rate"] > missing_rate_threshold, _MISSING_RATE_COLS
|
|
397
|
+
].copy()
|
|
398
|
+
|
|
399
|
+
n_before = len(current)
|
|
400
|
+
current = _apply_stage_keep(
|
|
401
|
+
current,
|
|
402
|
+
keep,
|
|
403
|
+
"missing_rate",
|
|
404
|
+
summary_rows,
|
|
405
|
+
on_empty_stage=on_empty_stage,
|
|
406
|
+
weight_col=weight_col,
|
|
407
|
+
threshold=missing_rate_threshold,
|
|
408
|
+
intersect=True,
|
|
409
|
+
)
|
|
410
|
+
summary_rows.append(
|
|
411
|
+
_summary_row("missing_rate", n_before, len(current), missing_rate_threshold, weight_col),
|
|
412
|
+
)
|
|
413
|
+
return current, missing_rate_table, missing_rate_dropped
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _apply_stage_keep(
|
|
417
|
+
current: list[str],
|
|
418
|
+
keep: list[str],
|
|
419
|
+
stage: str,
|
|
420
|
+
summary_rows: list[dict],
|
|
421
|
+
*,
|
|
422
|
+
on_empty_stage: Literal["keep_all_warn", "raise"] = "keep_all_warn",
|
|
423
|
+
weight_col: str | None = None,
|
|
424
|
+
threshold: Any = None,
|
|
425
|
+
intersect: bool = False,
|
|
426
|
+
) -> list[str]:
|
|
427
|
+
if intersect:
|
|
428
|
+
keep_set = set(keep)
|
|
429
|
+
new_current = [v for v in current if v in keep_set]
|
|
430
|
+
else:
|
|
431
|
+
new_current = list(keep) if keep else []
|
|
432
|
+
if new_current:
|
|
433
|
+
return new_current
|
|
434
|
+
if on_empty_stage == "raise":
|
|
435
|
+
raise ValueError(
|
|
436
|
+
f"feature screen stage {stage!r} eliminated all {len(current)} variables; "
|
|
437
|
+
f"on_empty_stage='raise'"
|
|
438
|
+
)
|
|
439
|
+
warnings.warn(
|
|
440
|
+
f"[feature_screen] stage {stage!r} 全军覆没, 按 keep_all_warn 保留全部 {len(current)} 个",
|
|
441
|
+
stacklevel=3,
|
|
442
|
+
)
|
|
443
|
+
summary_rows.append(
|
|
444
|
+
_summary_row(f"{stage}_fallback", len(current), len(current), threshold, weight_col)
|
|
445
|
+
)
|
|
446
|
+
return current
|
|
203
447
|
|
|
204
448
|
|
|
205
449
|
def _high_corr_pairs(varlist: list[str], corr: np.ndarray, threshold: float) -> pd.DataFrame:
|
|
@@ -280,12 +524,23 @@ def _legacy_unweighted_screen(
|
|
|
280
524
|
plot_path: str | None,
|
|
281
525
|
plot_outputs: bool,
|
|
282
526
|
iv_equal_freq: bool = True,
|
|
527
|
+
on_empty_stage: Literal["keep_all_warn", "raise"] = "keep_all_warn",
|
|
528
|
+
missing_rate_threshold: float | None = None,
|
|
529
|
+
missing_rate_ref: Any = None,
|
|
283
530
|
) -> WeightedScreenResult:
|
|
284
531
|
from Modeling_Tool import CorrelationFilter, PSICalculator, VarExtractionInsights
|
|
285
532
|
|
|
286
533
|
ins, oos, oot = splits["ins"], splits["oos"], splits["oot"]
|
|
287
534
|
current = list(feature_cols)
|
|
288
535
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, None)]
|
|
536
|
+
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
537
|
+
ins,
|
|
538
|
+
current,
|
|
539
|
+
summary_rows,
|
|
540
|
+
missing_rate_threshold=missing_rate_threshold,
|
|
541
|
+
missing_rate_ref=missing_rate_ref,
|
|
542
|
+
on_empty_stage=on_empty_stage,
|
|
543
|
+
)
|
|
289
544
|
|
|
290
545
|
psi_table = pd.DataFrame(columns=["var", "psi_ins_oos", "psi_ins_oot", "psi_max"])
|
|
291
546
|
if psi_enabled:
|
|
@@ -314,7 +569,10 @@ def _legacy_unweighted_screen(
|
|
|
314
569
|
psi_table["psi_max"] = psi_table[compare_cols].max(axis=1, skipna=True)
|
|
315
570
|
keep = psi_table.loc[psi_table["psi_max"] < psi_threshold, "var"].tolist()
|
|
316
571
|
n_before = len(current)
|
|
317
|
-
current =
|
|
572
|
+
current = _apply_stage_keep(
|
|
573
|
+
current, keep, "psi", summary_rows,
|
|
574
|
+
on_empty_stage=on_empty_stage, threshold=psi_threshold,
|
|
575
|
+
)
|
|
318
576
|
summary_rows.append(_summary_row("psi", n_before, len(current), psi_threshold, None))
|
|
319
577
|
|
|
320
578
|
iv_table = pd.DataFrame(columns=["var", "iv_weighted", "n_bins", "missing_rate"])
|
|
@@ -342,7 +600,10 @@ def _legacy_unweighted_screen(
|
|
|
342
600
|
iv_table = iv.rename(columns={"iv": "iv_weighted"})[["var", "iv_weighted", "n_bins", "missing_rate"]]
|
|
343
601
|
keep = iv.loc[iv["iv"] >= iv_threshold, "var"].tolist()
|
|
344
602
|
n_before = len(current)
|
|
345
|
-
current =
|
|
603
|
+
current = _apply_stage_keep(
|
|
604
|
+
current, keep, "iv", summary_rows,
|
|
605
|
+
on_empty_stage=on_empty_stage, threshold=iv_threshold,
|
|
606
|
+
)
|
|
346
607
|
summary_rows.append(_summary_row("iv", n_before, len(current), iv_threshold, None))
|
|
347
608
|
|
|
348
609
|
corr_dropped = pd.DataFrame(columns=["var_a", "var_b", "corr", "iv_a", "iv_b", "kept", "dropped"])
|
|
@@ -363,6 +624,8 @@ def _legacy_unweighted_screen(
|
|
|
363
624
|
psi_table=psi_table,
|
|
364
625
|
corr_dropped=corr_dropped,
|
|
365
626
|
summary=pd.DataFrame(summary_rows),
|
|
627
|
+
missing_rate_table=missing_rate_table,
|
|
628
|
+
missing_rate_dropped=missing_rate_dropped,
|
|
366
629
|
)
|
|
367
630
|
|
|
368
631
|
|
|
@@ -384,6 +647,12 @@ def _weighted_screen_impl(
|
|
|
384
647
|
corr_max_iterations: int,
|
|
385
648
|
content: float,
|
|
386
649
|
precision: int,
|
|
650
|
+
corr_use_woe_bins: bool = False,
|
|
651
|
+
corr_nan_policy: Literal["pairwise", "median_fill", "raise"] = "pairwise",
|
|
652
|
+
on_empty_stage: Literal["keep_all_warn", "raise"] = "keep_all_warn",
|
|
653
|
+
prefit_woe_engine: Any | None = None,
|
|
654
|
+
missing_rate_threshold: float | None = None,
|
|
655
|
+
missing_rate_ref: Any = None,
|
|
387
656
|
) -> WeightedScreenResult:
|
|
388
657
|
ins = splits["ins"]
|
|
389
658
|
oos = splits["oos"]
|
|
@@ -392,6 +661,15 @@ def _weighted_screen_impl(
|
|
|
392
661
|
|
|
393
662
|
current = list(feature_cols)
|
|
394
663
|
summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, weight_col)]
|
|
664
|
+
current, missing_rate_table, missing_rate_dropped = _apply_missing_rate_stage(
|
|
665
|
+
ins,
|
|
666
|
+
current,
|
|
667
|
+
summary_rows,
|
|
668
|
+
missing_rate_threshold=missing_rate_threshold,
|
|
669
|
+
missing_rate_ref=missing_rate_ref,
|
|
670
|
+
weight_col=weight_col,
|
|
671
|
+
on_empty_stage=on_empty_stage,
|
|
672
|
+
)
|
|
395
673
|
|
|
396
674
|
# Precompute bin edges on ins for PSI / IV
|
|
397
675
|
edges_cache: dict[str, list[float] | None] = {}
|
|
@@ -432,7 +710,11 @@ def _weighted_screen_impl(
|
|
|
432
710
|
psi_table["psi_max"] = psi_table[compare_cols].max(axis=1, skipna=True)
|
|
433
711
|
keep = psi_table.loc[psi_table["psi_max"] < psi_threshold, "var"].tolist()
|
|
434
712
|
n_before = len(current)
|
|
435
|
-
current =
|
|
713
|
+
current = _apply_stage_keep(
|
|
714
|
+
current, keep, "psi", summary_rows,
|
|
715
|
+
on_empty_stage=on_empty_stage, weight_col=weight_col,
|
|
716
|
+
threshold=psi_threshold, intersect=True,
|
|
717
|
+
)
|
|
436
718
|
summary_rows.append(_summary_row("psi", n_before, len(current), psi_threshold, weight_col))
|
|
437
719
|
else:
|
|
438
720
|
psi_table["psi_max"] = pd.Series(dtype=float)
|
|
@@ -464,15 +746,23 @@ def _weighted_screen_impl(
|
|
|
464
746
|
if not iv_table.empty:
|
|
465
747
|
keep = iv_table.loc[iv_table["iv_weighted"] >= iv_threshold, "var"].tolist()
|
|
466
748
|
n_before = len(current)
|
|
467
|
-
current =
|
|
749
|
+
current = _apply_stage_keep(
|
|
750
|
+
current, keep, "iv", summary_rows,
|
|
751
|
+
on_empty_stage=on_empty_stage, weight_col=weight_col,
|
|
752
|
+
threshold=iv_threshold, intersect=True,
|
|
753
|
+
)
|
|
468
754
|
summary_rows.append(_summary_row("iv", n_before, len(current), iv_threshold, weight_col))
|
|
469
755
|
else:
|
|
470
756
|
iv_table = pd.DataFrame(columns=["var", "iv_weighted", "n_bins", "missing_rate"])
|
|
471
757
|
|
|
472
758
|
corr_dropped = pd.DataFrame(columns=["var_a", "var_b", "corr", "iv_a", "iv_b", "kept", "dropped"])
|
|
473
759
|
if corr_enabled and len(current) > 1:
|
|
474
|
-
|
|
475
|
-
|
|
760
|
+
corr = _weighted_corr_for_screen(
|
|
761
|
+
ins, current, w_ins,
|
|
762
|
+
corr_use_woe_bins=corr_use_woe_bins,
|
|
763
|
+
corr_nan_policy=corr_nan_policy,
|
|
764
|
+
binner=prefit_woe_engine,
|
|
765
|
+
)
|
|
476
766
|
iv_map = dict(zip(iv_table["var"], iv_table["iv_weighted"])) if not iv_table.empty else {}
|
|
477
767
|
n_before = len(current)
|
|
478
768
|
current, corr_dropped = _corr_dedup_weighted(
|
|
@@ -487,6 +777,8 @@ def _weighted_screen_impl(
|
|
|
487
777
|
psi_table=psi_table,
|
|
488
778
|
corr_dropped=corr_dropped,
|
|
489
779
|
summary=pd.DataFrame(summary_rows),
|
|
780
|
+
missing_rate_table=missing_rate_table,
|
|
781
|
+
missing_rate_dropped=missing_rate_dropped,
|
|
490
782
|
)
|
|
491
783
|
|
|
492
784
|
|
|
@@ -521,6 +813,8 @@ def weighted_feature_screen(
|
|
|
521
813
|
woe_params: dict[str, Any] | None = None,
|
|
522
814
|
monotone_woe_params: dict[str, Any] | None = None,
|
|
523
815
|
prefit_woe_engine: Any | None = None,
|
|
816
|
+
corr_nan_policy: Literal["pairwise", "median_fill", "raise"] = "pairwise",
|
|
817
|
+
on_empty_stage: Literal["keep_all_warn", "raise"] = "keep_all_warn",
|
|
524
818
|
) -> WeightedScreenResult:
|
|
525
819
|
"""Run PSI -> IV -> correlation screening with optional sample weights.
|
|
526
820
|
|
|
@@ -563,6 +857,8 @@ def weighted_feature_screen(
|
|
|
563
857
|
content=content,
|
|
564
858
|
precision=precision,
|
|
565
859
|
min_bin_prop=min_bin_prop,
|
|
860
|
+
corr_nan_policy=corr_nan_policy,
|
|
861
|
+
on_empty_stage=on_empty_stage,
|
|
566
862
|
)
|
|
567
863
|
return feature_screen_from_dataframe(
|
|
568
864
|
data,
|
|
@@ -97,6 +97,7 @@ class FeatureValidationPipelineConfig:
|
|
|
97
97
|
}
|
|
98
98
|
)
|
|
99
99
|
|
|
100
|
+
missing_rate_threshold: float | None = None
|
|
100
101
|
selection_enabled: bool = False
|
|
101
102
|
selection_params: dict[str, Any] = field(default_factory=dict)
|
|
102
103
|
weight_col: str | None = None
|
|
@@ -1174,6 +1175,7 @@ class FeatureValidationPipeline:
|
|
|
1174
1175
|
params = dict(cfg.selection_params or {})
|
|
1175
1176
|
corr_params = dict(cfg.corr_params or {})
|
|
1176
1177
|
psi_params = dict(cfg.psi_params or {})
|
|
1178
|
+
missing_rate_threshold = params.get("missing_rate_threshold", cfg.missing_rate_threshold)
|
|
1177
1179
|
mapping = {
|
|
1178
1180
|
"psi_enabled": params.get("psi_enabled", cfg.psi_enabled),
|
|
1179
1181
|
"psi_threshold": params.get("psi_threshold", 0.2),
|
|
@@ -1196,6 +1198,11 @@ class FeatureValidationPipeline:
|
|
|
1196
1198
|
corr_params.get("max_iterations", 10),
|
|
1197
1199
|
),
|
|
1198
1200
|
"corr_use_woe_bins": params.get("corr_use_woe_bins", cfg.corr_use_woe_bins),
|
|
1201
|
+
"missing_rate_threshold": missing_rate_threshold,
|
|
1202
|
+
"missing_rate_ref": params.get(
|
|
1203
|
+
"missing_rate_ref",
|
|
1204
|
+
cfg.woe_params.get("missing_ref_value", -999999),
|
|
1205
|
+
),
|
|
1199
1206
|
}
|
|
1200
1207
|
return screen_config_from_mapping(
|
|
1201
1208
|
mapping,
|
|
@@ -1238,6 +1245,7 @@ class FeatureValidationPipeline:
|
|
|
1238
1245
|
config_snapshot = {
|
|
1239
1246
|
"selection_enabled": True,
|
|
1240
1247
|
"selection_params": dict(cfg.selection_params or {}),
|
|
1248
|
+
"missing_rate_threshold": screen_cfg.missing_rate_threshold,
|
|
1241
1249
|
"weight_col": cfg.weight_col,
|
|
1242
1250
|
"psi_use_woe_bins": screen_cfg.psi_use_woe_bins,
|
|
1243
1251
|
"iv_use_woe_bins": screen_cfg.iv_use_woe_bins,
|
|
@@ -1673,9 +1681,15 @@ class FeatureValidationPipeline:
|
|
|
1673
1681
|
}
|
|
1674
1682
|
if selected_features:
|
|
1675
1683
|
tables["selected_features"] = pd.DataFrame({"feature": selected_features})
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1684
|
+
if selection_summary:
|
|
1685
|
+
for key in ("missing_rate", "missing_rate_dropped", "screen_summary"):
|
|
1686
|
+
value = selection_summary.get(key)
|
|
1687
|
+
if isinstance(value, pd.DataFrame) and not value.empty:
|
|
1688
|
+
tables[f"selection_{key}"] = value
|
|
1689
|
+
if "final_features" in selection_summary:
|
|
1690
|
+
tables["selection_final_features"] = pd.DataFrame(
|
|
1691
|
+
{"feature": list(selection_summary["final_features"])},
|
|
1692
|
+
)
|
|
1679
1693
|
for name, df in distribution_summary.items():
|
|
1680
1694
|
tables[f"distribution_{name}"] = df
|
|
1681
1695
|
return tables
|
|
@@ -31,6 +31,10 @@ def screen_result_to_summary(
|
|
|
31
31
|
summary["iv"] = iv
|
|
32
32
|
if not result.corr_dropped.empty:
|
|
33
33
|
summary["corr_dropped"] = result.corr_dropped
|
|
34
|
+
if not result.missing_rate_table.empty:
|
|
35
|
+
summary["missing_rate"] = result.missing_rate_table
|
|
36
|
+
if not result.missing_rate_dropped.empty:
|
|
37
|
+
summary["missing_rate_dropped"] = result.missing_rate_dropped
|
|
34
38
|
summary["corr_features"] = list(result.selected_features)
|
|
35
39
|
summary["screen_summary"] = result.summary
|
|
36
40
|
summary["final_features"] = list(result.selected_features)
|
|
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
|
|
|
12
12
|
|
|
13
13
|
[project]
|
|
14
14
|
name = "SuperModelingFactory"
|
|
15
|
-
version = "0.3.
|
|
15
|
+
version = "0.3.15"
|
|
16
16
|
description = "Credit risk modeling factory: WOE binning, scorecards, LightGBM, Excel reporting."
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
requires-python = ">=3.10"
|
|
@@ -15,7 +15,7 @@ def _read(path: str) -> str:
|
|
|
15
15
|
|
|
16
16
|
setup(
|
|
17
17
|
name="SuperModelingFactory",
|
|
18
|
-
version=os.environ.get("SMF_VERSION", "0.3.
|
|
18
|
+
version=os.environ.get("SMF_VERSION", "0.3.15"),
|
|
19
19
|
description="Credit risk modeling factory: WOE binning, scorecards, LightGBM, Excel reporting.",
|
|
20
20
|
long_description=_read("README.md"),
|
|
21
21
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Binning_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Binning_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Parallel_Engine.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Proc_Compare.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Slope_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/Slope_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/XOR_Encryptor.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/XOR_Encryptor.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/kDataFrame.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Core/kDataFrame.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Evaluation_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Evaluation_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Model_Eval_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/Model_Eval_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/evaluate_model.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Eval/evaluate_model.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Explainability/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/PSI_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/PSI_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Feature/__init__.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/Backward_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/Backward_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/GBM_Search_Tool.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/GBM_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Model/LRM_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/__init__.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/_common.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/credit_model.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/mock_sample.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Pipeline/orchestrator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Reject_Infer.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Reject_Infer.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Sample_Split.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/Sample_Split.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/Sample/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Adapter.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Adapter.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Master.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Monotone_Binner.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Plot_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Report_Builder.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/WOE_Report_Builder.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/plot_woe_tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/WOE/plot_woe_tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/KaiTi.ttf
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/__init__.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.3.13 → supermodelingfactory-0.3.15}/Modeling_Tool/ref_font/simsun.ttc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|