SuperModelingFactory 0.2.0__py3-none-any.whl
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.
- ExcelMaster/ExcelFormatTool.py +487 -0
- ExcelMaster/ExcelMaster.py +917 -0
- ExcelMaster/Template.py +525 -0
- ExcelMaster/Utility.py +233 -0
- ExcelMaster/__init__.py +3 -0
- Modeling_Tool/Core/Binning_Tool.py +1608 -0
- Modeling_Tool/Core/Binning_Tool.pyi +48 -0
- Modeling_Tool/Core/Check_DuckDB_Compatibility.py +835 -0
- Modeling_Tool/Core/Json_Data_Converter.py +621 -0
- Modeling_Tool/Core/Model_Registry_Tool.py +199 -0
- Modeling_Tool/Core/ODPS_Tool.py +284 -0
- Modeling_Tool/Core/Slope_Tool.py +356 -0
- Modeling_Tool/Core/Slope_Tool.pyi +31 -0
- Modeling_Tool/Core/XOR_Encryptor.py +207 -0
- Modeling_Tool/Core/XOR_Encryptor.pyi +26 -0
- Modeling_Tool/Core/__init__.py +99 -0
- Modeling_Tool/Core/kDataFrame.py +228 -0
- Modeling_Tool/Core/kDataFrame.pyi +43 -0
- Modeling_Tool/Core/sample_weight_utils.py +77 -0
- Modeling_Tool/Core/utils.py +2672 -0
- Modeling_Tool/Eval/Evaluation_Tool.py +1452 -0
- Modeling_Tool/Eval/Evaluation_Tool.pyi +47 -0
- Modeling_Tool/Eval/Model_Eval_Tool.py +2548 -0
- Modeling_Tool/Eval/Model_Eval_Tool.pyi +37 -0
- Modeling_Tool/Eval/__init__.py +54 -0
- Modeling_Tool/Eval/evaluate_model.py +2008 -0
- Modeling_Tool/Eval/evaluate_model.pyi +50 -0
- Modeling_Tool/Eval/weighted_eval_utils.py +326 -0
- Modeling_Tool/Explainability/Coalition_Structure.py +305 -0
- Modeling_Tool/Explainability/Model_Explainer.py +743 -0
- Modeling_Tool/Explainability/__init__.py +24 -0
- Modeling_Tool/Feature/Distribution_Tool.py +509 -0
- Modeling_Tool/Feature/Distribution_Tool.pyi +42 -0
- Modeling_Tool/Feature/Feature_Insights.py +762 -0
- Modeling_Tool/Feature/Feature_Insights.pyi +33 -0
- Modeling_Tool/Feature/PSI_Tool.py +1195 -0
- Modeling_Tool/Feature/PSI_Tool.pyi +29 -0
- Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +355 -0
- Modeling_Tool/Feature/__init__.py +40 -0
- Modeling_Tool/Model/Backward_Tool.py +778 -0
- Modeling_Tool/Model/Backward_Tool.pyi +45 -0
- Modeling_Tool/Model/GBM_Search_Tool.py +251 -0
- Modeling_Tool/Model/GBM_Tool.py +1610 -0
- Modeling_Tool/Model/GBM_Tool.pyi +90 -0
- Modeling_Tool/Model/LRM_Tool.py +1198 -0
- Modeling_Tool/Model/LRM_Tool.pyi +47 -0
- Modeling_Tool/Model/__init__.py +61 -0
- Modeling_Tool/Sample/Distribution_Adaptation.py +131 -0
- Modeling_Tool/Sample/Distribution_Adaptation.pyi +30 -0
- Modeling_Tool/Sample/Reject_Infer.py +413 -0
- Modeling_Tool/Sample/Reject_Infer.pyi +43 -0
- Modeling_Tool/Sample/Sample_Split.py +520 -0
- Modeling_Tool/Sample/Sample_Split.pyi +43 -0
- Modeling_Tool/Sample/__init__.py +31 -0
- Modeling_Tool/UAT/UAT_Consistency_Checker.py +1180 -0
- Modeling_Tool/UAT/__init__.py +19 -0
- Modeling_Tool/WOE/WOE_Adapter.py +204 -0
- Modeling_Tool/WOE/WOE_Adapter.pyi +21 -0
- Modeling_Tool/WOE/WOE_Master.py +491 -0
- Modeling_Tool/WOE/WOE_Master.pyi +40 -0
- Modeling_Tool/WOE/WOE_Monotone_Binner.py +3324 -0
- Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +71 -0
- Modeling_Tool/WOE/WOE_Plot_Tool.py +919 -0
- Modeling_Tool/WOE/WOE_Plot_Tool.pyi +46 -0
- Modeling_Tool/WOE/WOE_Report_Builder.py +214 -0
- Modeling_Tool/WOE/WOE_Report_Builder.pyi +24 -0
- Modeling_Tool/WOE/WOE_Tool.py +1094 -0
- Modeling_Tool/WOE/WOE_Tool.pyi +41 -0
- Modeling_Tool/WOE/__init__.py +86 -0
- Modeling_Tool/WOE/plot_woe_tool.py +290 -0
- Modeling_Tool/WOE/plot_woe_tool.pyi +25 -0
- Modeling_Tool/__init__.py +176 -0
- Report/Report_Tool.py +380 -0
- Report/__init__.py +3 -0
- supermodelingfactory-0.2.0.dist-info/METADATA +268 -0
- supermodelingfactory-0.2.0.dist-info/RECORD +79 -0
- supermodelingfactory-0.2.0.dist-info/WHEEL +5 -0
- supermodelingfactory-0.2.0.dist-info/licenses/LICENSE +102 -0
- supermodelingfactory-0.2.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .UAT_Consistency_Checker import (
|
|
2
|
+
UATConfig,
|
|
3
|
+
UATConsistencyChecker,
|
|
4
|
+
safe_diff,
|
|
5
|
+
safe_eq,
|
|
6
|
+
mismatch_mask,
|
|
7
|
+
time_diff_seconds,
|
|
8
|
+
time_mismatch_mask,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'UATConfig',
|
|
13
|
+
'UATConsistencyChecker',
|
|
14
|
+
'safe_diff',
|
|
15
|
+
'safe_eq',
|
|
16
|
+
'mismatch_mask',
|
|
17
|
+
'time_diff_seconds',
|
|
18
|
+
'time_mismatch_mask',
|
|
19
|
+
]
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Unified adapters for WOE binning engines.
|
|
2
|
+
|
|
3
|
+
The public toolkit has two WOE engines with different persistence formats:
|
|
4
|
+
``WOE_Master`` exposes a mapping table, while ``MonotoneWOEBinner`` exposes
|
|
5
|
+
``get_final_bins`` and ``apply_woe``. This module gives feature screening and
|
|
6
|
+
monitoring tools one small protocol to depend on instead of branching on each
|
|
7
|
+
engine implementation. The adapter is intentionally read-only with respect to
|
|
8
|
+
fitting: callers fit the engine once, then reuse it downstream.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Any, Iterable, Optional
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
import pandas as pd
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_CANONICAL_COLUMNS = [
|
|
21
|
+
"VAR",
|
|
22
|
+
"BIN_NUM",
|
|
23
|
+
"BIN_RANGE",
|
|
24
|
+
"MIN",
|
|
25
|
+
"MAX",
|
|
26
|
+
"N",
|
|
27
|
+
"N_BAD",
|
|
28
|
+
"N_GOOD",
|
|
29
|
+
"AVG_BAD",
|
|
30
|
+
"WOE",
|
|
31
|
+
"IV",
|
|
32
|
+
"IS_SPECIAL",
|
|
33
|
+
"ENGINE",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _upper_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
38
|
+
out = df.copy()
|
|
39
|
+
out.columns = [str(c).upper() for c in out.columns]
|
|
40
|
+
return out
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _first_existing(df: pd.DataFrame, names: Iterable[str]) -> Optional[str]:
|
|
44
|
+
cols = set(df.columns)
|
|
45
|
+
for name in names:
|
|
46
|
+
if name in cols:
|
|
47
|
+
return name
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _coerce_woe_frame(df: pd.DataFrame, var: Optional[str], engine: str) -> pd.DataFrame:
|
|
52
|
+
"""Normalize a single engine WOE table to the common column contract."""
|
|
53
|
+
if df is None or len(df) == 0:
|
|
54
|
+
return pd.DataFrame(columns=_CANONICAL_COLUMNS)
|
|
55
|
+
|
|
56
|
+
src = _upper_columns(pd.DataFrame(df))
|
|
57
|
+
out = pd.DataFrame(index=src.index)
|
|
58
|
+
|
|
59
|
+
var_col = _first_existing(src, ["VAR", "VARIABLE", "FEATURE", "FEATURE_NAME", "ATTRIBUTE"])
|
|
60
|
+
out["VAR"] = src[var_col] if var_col else var
|
|
61
|
+
|
|
62
|
+
bin_col = _first_existing(src, ["BIN_NUM", "BIN", "BIN_ID", "GROUP", "IDX"])
|
|
63
|
+
out["BIN_NUM"] = src[bin_col] if bin_col else np.arange(1, len(src) + 1)
|
|
64
|
+
|
|
65
|
+
range_col = _first_existing(src, ["BIN_RANGE", "RANGE", "BIN_LABEL", "LABEL", "CATEGORY", "CATE", "VALUE"])
|
|
66
|
+
out["BIN_RANGE"] = src[range_col] if range_col else out["BIN_NUM"].astype(str)
|
|
67
|
+
|
|
68
|
+
for target, candidates in {
|
|
69
|
+
"MIN": ["MIN", "LEFT", "LOWER", "LOWER_BOUND", "START"],
|
|
70
|
+
"MAX": ["MAX", "RIGHT", "UPPER", "UPPER_BOUND", "END"],
|
|
71
|
+
"N": ["N", "COUNT", "TOTAL", "TOTAL_COUNT", "CNT"],
|
|
72
|
+
"N_BAD": ["N_BAD", "BAD", "BAD_COUNT", "TARGET", "TARGET_COUNT"],
|
|
73
|
+
"N_GOOD": ["N_GOOD", "GOOD", "GOOD_COUNT", "NON_TARGET", "NON_TARGET_COUNT"],
|
|
74
|
+
"AVG_BAD": ["AVG_BAD", "BAD_RATE", "BADRATE", "TARGET_RATE", "EVENT_RATE"],
|
|
75
|
+
"WOE": ["WOE", "WOE_VALUE"],
|
|
76
|
+
"IV": ["IV", "IV_VALUE"],
|
|
77
|
+
}.items():
|
|
78
|
+
col = _first_existing(src, candidates)
|
|
79
|
+
out[target] = src[col] if col else np.nan
|
|
80
|
+
|
|
81
|
+
special_col = _first_existing(src, ["IS_SPECIAL", "SPECIAL", "SPECIAL_BIN"])
|
|
82
|
+
if special_col:
|
|
83
|
+
out["IS_SPECIAL"] = src[special_col].astype(bool)
|
|
84
|
+
else:
|
|
85
|
+
out["IS_SPECIAL"] = out["BIN_RANGE"].astype(str).str.lower().str.contains("special|missing|nan")
|
|
86
|
+
|
|
87
|
+
out["ENGINE"] = engine
|
|
88
|
+
return out[_CANONICAL_COLUMNS]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class WOEEngineAdapter:
|
|
93
|
+
"""Small protocol wrapper used by Feature and monitoring tools."""
|
|
94
|
+
|
|
95
|
+
engine: Any
|
|
96
|
+
engine_name: str
|
|
97
|
+
woe_suffix: str = "_woe"
|
|
98
|
+
|
|
99
|
+
def transform(self, data: pd.DataFrame, varlist: Optional[list[str]] = None, suffix: str = "_woe") -> pd.DataFrame:
|
|
100
|
+
raise NotImplementedError
|
|
101
|
+
|
|
102
|
+
def assign_bins(self, data: pd.DataFrame, var: str) -> pd.Series:
|
|
103
|
+
"""Return stable bin labels for PSI-like distribution comparisons.
|
|
104
|
+
|
|
105
|
+
For both engines, the WOE value is a stable fitted-bin proxy. This keeps
|
|
106
|
+
the method independent from each engine's private bin-label internals.
|
|
107
|
+
"""
|
|
108
|
+
transformed = self.transform(data, [var], suffix=self.woe_suffix)
|
|
109
|
+
woe_col = f"{var}{self.woe_suffix}"
|
|
110
|
+
if woe_col not in transformed.columns:
|
|
111
|
+
raise KeyError(f"WOE column {woe_col!r} was not produced by {self.engine_name}")
|
|
112
|
+
return transformed[woe_col].map(lambda x: "__MISSING__" if pd.isna(x) else str(x))
|
|
113
|
+
|
|
114
|
+
def get_woe_table(self, varlist: Optional[list[str]] = None) -> pd.DataFrame:
|
|
115
|
+
raise NotImplementedError
|
|
116
|
+
|
|
117
|
+
def get_bin_edges(self, varlist: Optional[list[str]] = None) -> dict[str, list[float]]:
|
|
118
|
+
return {}
|
|
119
|
+
|
|
120
|
+
def get_engine_name(self) -> str:
|
|
121
|
+
return self.engine_name
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class WOEMasterAdapter(WOEEngineAdapter):
|
|
125
|
+
def __init__(self, engine: Any, woe_suffix: str = "_woe"):
|
|
126
|
+
super().__init__(engine=engine, engine_name="master", woe_suffix=woe_suffix)
|
|
127
|
+
|
|
128
|
+
def transform(self, data: pd.DataFrame, varlist: Optional[list[str]] = None, suffix: str = "_woe") -> pd.DataFrame:
|
|
129
|
+
try:
|
|
130
|
+
return self.engine.transform(data=data, varlist=varlist)
|
|
131
|
+
except TypeError:
|
|
132
|
+
return self.engine.transform(data, varlist)
|
|
133
|
+
|
|
134
|
+
def get_woe_table(self, varlist: Optional[list[str]] = None) -> pd.DataFrame:
|
|
135
|
+
table = self.engine.get_mapping_table()
|
|
136
|
+
out = _coerce_woe_frame(table, None, self.engine_name)
|
|
137
|
+
if varlist is not None:
|
|
138
|
+
out = out[out["VAR"].isin(varlist)]
|
|
139
|
+
return out.reset_index(drop=True)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class MonotoneBinnerAdapter(WOEEngineAdapter):
|
|
143
|
+
def __init__(self, engine: Any, woe_suffix: str = "_woe"):
|
|
144
|
+
super().__init__(engine=engine, engine_name="monotone", woe_suffix=woe_suffix)
|
|
145
|
+
|
|
146
|
+
def transform(self, data: pd.DataFrame, varlist: Optional[list[str]] = None, suffix: str = "_woe") -> pd.DataFrame:
|
|
147
|
+
transformed = self.engine.apply_woe(data, suffix=suffix, inplace=False)
|
|
148
|
+
if varlist is None:
|
|
149
|
+
return transformed
|
|
150
|
+
keep = list(data.columns) + [f"{v}{suffix}" for v in varlist if f"{v}{suffix}" in transformed.columns]
|
|
151
|
+
return transformed.loc[:, list(dict.fromkeys([c for c in keep if c in transformed.columns]))]
|
|
152
|
+
|
|
153
|
+
def get_woe_table(self, varlist: Optional[list[str]] = None) -> pd.DataFrame:
|
|
154
|
+
bins = self.engine.get_final_bins()
|
|
155
|
+
frames = []
|
|
156
|
+
selected = set(varlist) if varlist is not None else None
|
|
157
|
+
for var, df in bins.items():
|
|
158
|
+
if selected is not None and var not in selected:
|
|
159
|
+
continue
|
|
160
|
+
frames.append(_coerce_woe_frame(df, var, self.engine_name))
|
|
161
|
+
if not frames:
|
|
162
|
+
return pd.DataFrame(columns=_CANONICAL_COLUMNS)
|
|
163
|
+
return pd.concat(frames, ignore_index=True)[_CANONICAL_COLUMNS]
|
|
164
|
+
|
|
165
|
+
def get_bin_edges(self, varlist: Optional[list[str]] = None) -> dict[str, list[float]]:
|
|
166
|
+
if not hasattr(self.engine, "get_bin_edges"):
|
|
167
|
+
return {}
|
|
168
|
+
edges = self.engine.get_bin_edges()
|
|
169
|
+
if varlist is None:
|
|
170
|
+
return edges
|
|
171
|
+
return {k: v for k, v in edges.items() if k in set(varlist)}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def as_woe_engine(engine: Any, woe_suffix: str = "_woe") -> Optional[WOEEngineAdapter]:
|
|
175
|
+
"""Return a unified adapter for supported fitted WOE engines.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
engine:
|
|
180
|
+
``WOE_Master``, ``MonotoneWOEBinner`` or an existing adapter. ``None`` is
|
|
181
|
+
returned unchanged so callers can preserve legacy behavior.
|
|
182
|
+
woe_suffix:
|
|
183
|
+
Suffix used for generated WOE columns.
|
|
184
|
+
"""
|
|
185
|
+
if engine is None:
|
|
186
|
+
return None
|
|
187
|
+
if isinstance(engine, WOEEngineAdapter):
|
|
188
|
+
return engine
|
|
189
|
+
if hasattr(engine, "get_mapping_table") and hasattr(engine, "transform"):
|
|
190
|
+
return WOEMasterAdapter(engine, woe_suffix=woe_suffix)
|
|
191
|
+
if hasattr(engine, "get_final_bins") and hasattr(engine, "apply_woe"):
|
|
192
|
+
return MonotoneBinnerAdapter(engine, woe_suffix=woe_suffix)
|
|
193
|
+
raise TypeError(
|
|
194
|
+
"Unsupported WOE engine. Expected WOE_Master, MonotoneWOEBinner, "
|
|
195
|
+
"or WOEEngineAdapter."
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
__all__ = [
|
|
200
|
+
"WOEEngineAdapter",
|
|
201
|
+
"WOEMasterAdapter",
|
|
202
|
+
"MonotoneBinnerAdapter",
|
|
203
|
+
"as_woe_engine",
|
|
204
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any, Optional
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
class WOEEngineAdapter:
|
|
6
|
+
engine: Any
|
|
7
|
+
engine_name: str
|
|
8
|
+
woe_suffix: str
|
|
9
|
+
def transform(self, data: pd.DataFrame, varlist: Optional[list[str]] = None, suffix: str = '_woe') -> pd.DataFrame: ...
|
|
10
|
+
def assign_bins(self, data: pd.DataFrame, var: str) -> pd.Series: ...
|
|
11
|
+
def get_woe_table(self, varlist: Optional[list[str]] = None) -> pd.DataFrame: ...
|
|
12
|
+
def get_bin_edges(self, varlist: Optional[list[str]] = None) -> dict[str, list[float]]: ...
|
|
13
|
+
def get_engine_name(self) -> str: ...
|
|
14
|
+
|
|
15
|
+
class WOEMasterAdapter(WOEEngineAdapter):
|
|
16
|
+
def __init__(self, engine: Any, woe_suffix: str = '_woe'): ...
|
|
17
|
+
|
|
18
|
+
class MonotoneBinnerAdapter(WOEEngineAdapter):
|
|
19
|
+
def __init__(self, engine: Any, woe_suffix: str = '_woe'): ...
|
|
20
|
+
|
|
21
|
+
def as_woe_engine(engine: Any, woe_suffix: str = '_woe') -> Optional[WOEEngineAdapter]: ...
|