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.
Files changed (79) hide show
  1. ExcelMaster/ExcelFormatTool.py +487 -0
  2. ExcelMaster/ExcelMaster.py +917 -0
  3. ExcelMaster/Template.py +525 -0
  4. ExcelMaster/Utility.py +233 -0
  5. ExcelMaster/__init__.py +3 -0
  6. Modeling_Tool/Core/Binning_Tool.py +1608 -0
  7. Modeling_Tool/Core/Binning_Tool.pyi +48 -0
  8. Modeling_Tool/Core/Check_DuckDB_Compatibility.py +835 -0
  9. Modeling_Tool/Core/Json_Data_Converter.py +621 -0
  10. Modeling_Tool/Core/Model_Registry_Tool.py +199 -0
  11. Modeling_Tool/Core/ODPS_Tool.py +284 -0
  12. Modeling_Tool/Core/Slope_Tool.py +356 -0
  13. Modeling_Tool/Core/Slope_Tool.pyi +31 -0
  14. Modeling_Tool/Core/XOR_Encryptor.py +207 -0
  15. Modeling_Tool/Core/XOR_Encryptor.pyi +26 -0
  16. Modeling_Tool/Core/__init__.py +99 -0
  17. Modeling_Tool/Core/kDataFrame.py +228 -0
  18. Modeling_Tool/Core/kDataFrame.pyi +43 -0
  19. Modeling_Tool/Core/sample_weight_utils.py +77 -0
  20. Modeling_Tool/Core/utils.py +2672 -0
  21. Modeling_Tool/Eval/Evaluation_Tool.py +1452 -0
  22. Modeling_Tool/Eval/Evaluation_Tool.pyi +47 -0
  23. Modeling_Tool/Eval/Model_Eval_Tool.py +2548 -0
  24. Modeling_Tool/Eval/Model_Eval_Tool.pyi +37 -0
  25. Modeling_Tool/Eval/__init__.py +54 -0
  26. Modeling_Tool/Eval/evaluate_model.py +2008 -0
  27. Modeling_Tool/Eval/evaluate_model.pyi +50 -0
  28. Modeling_Tool/Eval/weighted_eval_utils.py +326 -0
  29. Modeling_Tool/Explainability/Coalition_Structure.py +305 -0
  30. Modeling_Tool/Explainability/Model_Explainer.py +743 -0
  31. Modeling_Tool/Explainability/__init__.py +24 -0
  32. Modeling_Tool/Feature/Distribution_Tool.py +509 -0
  33. Modeling_Tool/Feature/Distribution_Tool.pyi +42 -0
  34. Modeling_Tool/Feature/Feature_Insights.py +762 -0
  35. Modeling_Tool/Feature/Feature_Insights.pyi +33 -0
  36. Modeling_Tool/Feature/PSI_Tool.py +1195 -0
  37. Modeling_Tool/Feature/PSI_Tool.pyi +29 -0
  38. Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +355 -0
  39. Modeling_Tool/Feature/__init__.py +40 -0
  40. Modeling_Tool/Model/Backward_Tool.py +778 -0
  41. Modeling_Tool/Model/Backward_Tool.pyi +45 -0
  42. Modeling_Tool/Model/GBM_Search_Tool.py +251 -0
  43. Modeling_Tool/Model/GBM_Tool.py +1610 -0
  44. Modeling_Tool/Model/GBM_Tool.pyi +90 -0
  45. Modeling_Tool/Model/LRM_Tool.py +1198 -0
  46. Modeling_Tool/Model/LRM_Tool.pyi +47 -0
  47. Modeling_Tool/Model/__init__.py +61 -0
  48. Modeling_Tool/Sample/Distribution_Adaptation.py +131 -0
  49. Modeling_Tool/Sample/Distribution_Adaptation.pyi +30 -0
  50. Modeling_Tool/Sample/Reject_Infer.py +413 -0
  51. Modeling_Tool/Sample/Reject_Infer.pyi +43 -0
  52. Modeling_Tool/Sample/Sample_Split.py +520 -0
  53. Modeling_Tool/Sample/Sample_Split.pyi +43 -0
  54. Modeling_Tool/Sample/__init__.py +31 -0
  55. Modeling_Tool/UAT/UAT_Consistency_Checker.py +1180 -0
  56. Modeling_Tool/UAT/__init__.py +19 -0
  57. Modeling_Tool/WOE/WOE_Adapter.py +204 -0
  58. Modeling_Tool/WOE/WOE_Adapter.pyi +21 -0
  59. Modeling_Tool/WOE/WOE_Master.py +491 -0
  60. Modeling_Tool/WOE/WOE_Master.pyi +40 -0
  61. Modeling_Tool/WOE/WOE_Monotone_Binner.py +3324 -0
  62. Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +71 -0
  63. Modeling_Tool/WOE/WOE_Plot_Tool.py +919 -0
  64. Modeling_Tool/WOE/WOE_Plot_Tool.pyi +46 -0
  65. Modeling_Tool/WOE/WOE_Report_Builder.py +214 -0
  66. Modeling_Tool/WOE/WOE_Report_Builder.pyi +24 -0
  67. Modeling_Tool/WOE/WOE_Tool.py +1094 -0
  68. Modeling_Tool/WOE/WOE_Tool.pyi +41 -0
  69. Modeling_Tool/WOE/__init__.py +86 -0
  70. Modeling_Tool/WOE/plot_woe_tool.py +290 -0
  71. Modeling_Tool/WOE/plot_woe_tool.pyi +25 -0
  72. Modeling_Tool/__init__.py +176 -0
  73. Report/Report_Tool.py +380 -0
  74. Report/__init__.py +3 -0
  75. supermodelingfactory-0.2.0.dist-info/METADATA +268 -0
  76. supermodelingfactory-0.2.0.dist-info/RECORD +79 -0
  77. supermodelingfactory-0.2.0.dist-info/WHEEL +5 -0
  78. supermodelingfactory-0.2.0.dist-info/licenses/LICENSE +102 -0
  79. supermodelingfactory-0.2.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,29 @@
1
+ # =============================================================================
2
+ # Modeling_Tool.Feature.PSI_Tool
3
+ # -----------------------------------------------------------------------------
4
+ # Copyright (c) 2026 Kyle Sun <github.com/Kyle-J-Sun>. All rights reserved.
5
+ # SuperModelingFactory — Licensed under the Business Source License 1.1.
6
+ #
7
+ # This stub describes the public API of a closed-source module compiled to a
8
+ # native extension (.so / .pyd). The original source is not distributed.
9
+ # Production / commercial use requires a separate commercial license.
10
+ #
11
+ # FINGERPRINT: SMF-PSITOOL-5299b9cc
12
+ # (Unique trace marker. Do not remove or alter — used for plagiarism
13
+ # detection across the public internet.)
14
+ # =============================================================================
15
+
16
+ import numpy as np
17
+ import pandas as pd
18
+ from typing import Union, List, Dict, Optional, Tuple, Callable, Any
19
+ from tqdm import tqdm
20
+ from Modeling_Tool.Core.Binning_Tool import quick_binning
21
+
22
+ class PSICalculator:
23
+ def __init__(self, buckets: int = 10, equal_freq: bool = True, min_bin_prop: float = 0.05, content: float = 1e-06, precision: int = 5, binning_engine: Any = None): ...
24
+ def calculate(self, expected_df: pd.DataFrame, current_data: pd.DataFrame, varlist: List[str], group_by: Optional[str] = None, group_name: Optional[str] = None, return_details = False) -> pd.DataFrame: ...
25
+ def calculate_psi(expected: Union[pd.DataFrame, pd.Series], actual: Union[pd.DataFrame, pd.Series], target_col: str, buckets: int = 10, equal_freq: bool = True, group_by: Optional[Union[str, List[str]]] = None, return_details: bool = False, min_bin_prop: float = 0.05, content: float = 1e-06, precision: int = 5) -> Union[float, pd.DataFrame, Tuple[Dict, Dict]]: ...
26
+ def calculate_within_psi(data: pd.DataFrame, grp_name: str, target_col: str, benchmark: Optional[Any] = None, equal_freq: bool = True, buckets: int = 10, return_details: bool = False, min_bin_prop: float = 0.05, content: float = 1e-06, precision: int = 5, benchmark_display_name: Optional[str] = None) -> Union[pd.DataFrame, Dict]: ...
27
+ def calculate_psi_within_dataset(data: pd.DataFrame, grp_name: str, varlist: List[str], benchmark: Optional[Any] = None, equal_freq: bool = True, buckets: int = 10, min_bin_prop: float = 0.05, content: float = 1e-06, precision: int = 5) -> pd.DataFrame: ...
28
+ def calculate_multivar_psi_two_sets(expected_df: pd.DataFrame, actual_df: pd.DataFrame, varlist: List[str], group_by: Optional[Union[str, List[str]]] = None, buckets: int = 10, equal_freq: bool = True, min_bin_prop: float = 0.05, content: float = 1e-06, precision: int = 5) -> pd.DataFrame: ...
29
+ def calculate_multigroup_psi_two_sets(expected_df: pd.DataFrame, actual_df: pd.DataFrame, varlist: List[str], group_by: Optional[Union[str, List[str]]] = None, buckets: int = 10, equal_freq: bool = True, min_bin_prop: float = 0.05, content: float = 1e-06, precision: int = 5, group_name: Optional[str] = None, return_details: bool = False) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: ...
@@ -0,0 +1,355 @@
1
+ """WOE-engine-aware wrappers for feature screening tools.
2
+
3
+ The original Feature modules are compiled in release builds, so mutating their
4
+ class objects at import time is fragile. These wrappers delegate to the original
5
+ classes for legacy behavior and only take over when a fitted WOE engine is
6
+ explicitly supplied.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import numpy as np
12
+ import pandas as pd
13
+ from tqdm import tqdm
14
+
15
+ from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
16
+ from .PSI_Tool import PSICalculator as _BasePSICalculator
17
+ from .Feature_Insights import (
18
+ CorrelationFilter as _BaseCorrelationFilter,
19
+ VarExtractionInsights as _BaseVarExtractionInsights,
20
+ var_corr_filter,
21
+ )
22
+ from .Distribution_Tool import proc_means_by_grp
23
+
24
+
25
+ def _psi_from_bins(expected_bins: pd.Series, current_bins: pd.Series, content: float) -> float:
26
+ expected_pct = expected_bins.value_counts(normalize=True, dropna=False)
27
+ current_pct = current_bins.value_counts(normalize=True, dropna=False)
28
+ all_bins = expected_pct.index.union(current_pct.index)
29
+ e = expected_pct.reindex(all_bins, fill_value=0).astype(float) + content
30
+ c = current_pct.reindex(all_bins, fill_value=0).astype(float) + content
31
+ return float(((c - e) * np.log(c / e)).sum())
32
+
33
+
34
+ def _make_monotone_adapter(owner, data: pd.DataFrame, varlist: list[str]):
35
+ if getattr(owner, "woe_binner", None) is not None:
36
+ return as_woe_engine(owner.woe_binner)
37
+ if getattr(owner, "woe_engine", "master") != "monotone":
38
+ return None
39
+
40
+ from Modeling_Tool.WOE.WOE_Monotone_Binner import MonotoneWOEBinner
41
+
42
+ params = dict(getattr(owner, "woe_engine_params", {}) or {})
43
+ fit_params = params.pop("fit_params", {}) if "fit_params" in params else {}
44
+ binner = MonotoneWOEBinner(
45
+ feature_cols=varlist,
46
+ target_col=owner.dep,
47
+ special_values=owner.spec_values,
48
+ **params,
49
+ )
50
+ binner.fit(
51
+ data,
52
+ chi2_binning=owner.chi2_method,
53
+ chi2_p=owner.chi2_p,
54
+ chi2_init_size=owner.init_equi_bins,
55
+ **fit_params,
56
+ )
57
+ owner.woe_binner = binner
58
+ return as_woe_engine(binner)
59
+
60
+
61
+ def _screening_summary_from_engine(
62
+ data: pd.DataFrame,
63
+ varlist: list[str],
64
+ dep: str,
65
+ adapter,
66
+ iv_cut: float,
67
+ missing_rate_ref,
68
+ ) -> pd.DataFrame:
69
+ rows = []
70
+ target = data[dep]
71
+ total_bad = max(float((target == 1).sum()), 1.0)
72
+ total_good = max(float((target == 0).sum()), 1.0)
73
+
74
+ for var in tqdm(varlist):
75
+ if var not in data.columns or data[var].nunique(dropna=False) <= 1:
76
+ continue
77
+ try:
78
+ bins = adapter.assign_bins(data, var)
79
+ tmp = pd.DataFrame({"bin": bins, dep: target})
80
+ grouped = tmp.groupby("bin", dropna=False)[dep].agg(["count", "sum"]).reset_index()
81
+ grouped = grouped.rename(columns={"count": "n", "sum": "n_bad"})
82
+ grouped["n_good"] = grouped["n"] - grouped["n_bad"]
83
+ grouped["bad_pct"] = grouped["n_bad"] / total_bad
84
+ grouped["good_pct"] = grouped["n_good"] / total_good
85
+ grouped["woe"] = np.log((grouped["bad_pct"] + 1e-6) / (grouped["good_pct"] + 1e-6))
86
+ grouped["iv"] = (grouped["bad_pct"] - grouped["good_pct"]) * grouped["woe"]
87
+ grouped["avg_bad"] = grouped["n_bad"] / grouped["n"].replace(0, np.nan)
88
+ grouped = grouped.sort_values("avg_bad", ascending=False).reset_index(drop=True)
89
+ ks = (grouped["bad_pct"].cumsum() - grouped["good_pct"].cumsum()).abs().max()
90
+ overall_bad = max(float((target == 1).mean()), 1e-6)
91
+ lift = float((grouped["avg_bad"] / overall_bad).replace([np.inf, -np.inf], np.nan).max())
92
+ rows.append({
93
+ "var": var,
94
+ "ks_in_gains": float(ks),
95
+ "lift_in_gains": lift,
96
+ "iv": float(grouped["iv"].sum()),
97
+ "n_bump": int(grouped.shape[0]),
98
+ "n_bins": int(grouped.shape[0]),
99
+ })
100
+ except Exception:
101
+ continue
102
+
103
+ columns = [
104
+ "var", "n_all", "n", "ks_in_gains", "lift_in_gains", "iv",
105
+ "n_bump", "missing_rate", "min", "mean", "max", "n_bins",
106
+ ]
107
+ if not rows:
108
+ return pd.DataFrame(columns=columns)
109
+
110
+ high_iv_summary = pd.DataFrame(rows).query(f"iv >= {iv_cut}").round(4)
111
+ high_iv_varlist = high_iv_summary["var"].tolist()
112
+ if high_iv_varlist:
113
+ means = proc_means_by_grp(data, high_iv_varlist, spec_missing_value=missing_rate_ref)
114
+ summary = high_iv_summary.merge(
115
+ means[["attribute", "N_ALL", "N", "MISSING_RATE", "MIN", "MEAN", "MAX"]],
116
+ left_on="var",
117
+ right_on="attribute",
118
+ how="left",
119
+ )
120
+ else:
121
+ summary = high_iv_summary.copy()
122
+ for col in ["N_ALL", "N", "MISSING_RATE", "MIN", "MEAN", "MAX"]:
123
+ summary[col] = np.nan
124
+
125
+ summary.columns = [str(c).lower() for c in summary.columns]
126
+ return summary[columns]
127
+
128
+
129
+ class PSICalculator:
130
+ def __init__(
131
+ self,
132
+ buckets: int = 10,
133
+ equal_freq: bool = True,
134
+ min_bin_prop: float = 0.05,
135
+ content: float = 1e-6,
136
+ precision: int = 5,
137
+ binning_engine=None,
138
+ ):
139
+ self._base = _BasePSICalculator(buckets, equal_freq, min_bin_prop, content, precision)
140
+ self.buckets = buckets
141
+ self.equal_freq = equal_freq
142
+ self.min_bin_prop = min_bin_prop
143
+ self.content = content
144
+ self.precision = precision
145
+ self.binning_engine = binning_engine
146
+ self._woe_engine_adapter = as_woe_engine(binning_engine) if binning_engine is not None else None
147
+
148
+ def __getattr__(self, name):
149
+ return getattr(self._base, name)
150
+
151
+ def calculate(self, expected_df, current_data, varlist, group_by=None, group_name=None, return_details=False):
152
+ adapter = self._woe_engine_adapter
153
+ if adapter is None:
154
+ return self._base.calculate(expected_df, current_data, varlist, group_by, group_name, return_details)
155
+
156
+ detail = {}
157
+ rows = []
158
+ groups = [(None, current_data)] if group_by is None else list(current_data.groupby(group_by))
159
+ for var in varlist:
160
+ expected_bins = adapter.assign_bins(expected_df, var)
161
+ for grp_value, grp_df in groups:
162
+ current_bins = adapter.assign_bins(grp_df, var)
163
+ row = {"var": var, "psi": round(_psi_from_bins(expected_bins, current_bins, self.content), self.precision)}
164
+ if group_by is not None:
165
+ row[group_name or group_by] = grp_value
166
+ rows.append(row)
167
+ if return_details:
168
+ detail[var if grp_value is None else (var, grp_value)] = {
169
+ "expected_bins": expected_bins.value_counts(normalize=True, dropna=False),
170
+ "current_bins": current_bins.value_counts(normalize=True, dropna=False),
171
+ }
172
+ result = pd.DataFrame(rows)
173
+ return (result, detail) if return_details else result
174
+
175
+
176
+ class VarExtractionInsights:
177
+ def __init__(
178
+ self,
179
+ data,
180
+ dep,
181
+ plot_path,
182
+ nbins=10,
183
+ equal_freq=True,
184
+ min_bin_prop=0.05,
185
+ precision=5,
186
+ chi2_method=False,
187
+ chi2_p=0.9,
188
+ init_equi_bins=5000,
189
+ tree_binning=True,
190
+ include_missing=True,
191
+ seed=3407,
192
+ missing_rate_ref=-999999,
193
+ spec_values=None,
194
+ woe_engine="master",
195
+ woe_binner=None,
196
+ woe_engine_params=None,
197
+ ):
198
+ self._base = _BaseVarExtractionInsights(
199
+ data, dep, plot_path, nbins, equal_freq, min_bin_prop, precision,
200
+ chi2_method, chi2_p, init_equi_bins, tree_binning, include_missing,
201
+ seed, missing_rate_ref, spec_values,
202
+ )
203
+ self.data = data
204
+ self.dep = dep
205
+ self.plot_path = plot_path
206
+ self.nbins = nbins
207
+ self.equal_freq = equal_freq
208
+ self.min_bin_prop = min_bin_prop
209
+ self.precision = precision
210
+ self.chi2_method = chi2_method
211
+ self.chi2_p = chi2_p
212
+ self.init_equi_bins = init_equi_bins
213
+ self.tree_binning = tree_binning
214
+ self.include_missing = include_missing
215
+ self.seed = seed
216
+ self.missing_rate_ref = missing_rate_ref
217
+ self.spec_values = spec_values if spec_values is not None else []
218
+ self.woe_engine = woe_engine
219
+ self.woe_binner = woe_binner
220
+ self.woe_engine_params = woe_engine_params or {}
221
+
222
+ def __getattr__(self, name):
223
+ return getattr(self._base, name)
224
+
225
+ @staticmethod
226
+ def remove_folder(file_path):
227
+ return _BaseVarExtractionInsights.remove_folder(file_path)
228
+
229
+ def get_var_analysis_report(self, data, varlist, dep=None, iv_cut=0.01):
230
+ if dep is None:
231
+ dep = self.dep
232
+ adapter = as_woe_engine(self.woe_binner) if self.woe_binner is not None else _make_monotone_adapter(self, data, varlist)
233
+ if adapter is None:
234
+ return self._base.get_var_analysis_report(data, varlist, dep, iv_cut)
235
+ return _screening_summary_from_engine(data, varlist, dep, adapter, iv_cut, self.missing_rate_ref)
236
+
237
+ def plot_woe(self, data, varlist, plot_group=None, plot_dirname="var_analysis_plot", plot_path=None):
238
+ adapter = as_woe_engine(self.woe_binner) if self.woe_binner is not None else _make_monotone_adapter(self, data, varlist)
239
+ if adapter is None:
240
+ return self._base.plot_woe(data, varlist, plot_group, plot_dirname, plot_path)
241
+ if plot_path is None:
242
+ plot_path = self.plot_path
243
+ if plot_path is None:
244
+ return None
245
+ if adapter.get_engine_name() == "monotone" and hasattr(adapter.engine, "plot_woe_graph"):
246
+ import os
247
+ graph_path = os.path.join(plot_path, plot_dirname)
248
+ adapter.engine.plot_woe_graph(graph_path, group_name=plot_group, _df_for_group=data if plot_group else None)
249
+ return None
250
+ return self._base.plot_woe(data, varlist, plot_group, plot_dirname, plot_path)
251
+
252
+
253
+ class CorrelationFilter:
254
+ def __init__(
255
+ self,
256
+ data,
257
+ dep,
258
+ corr_cutpoint=0.8,
259
+ method="pearson",
260
+ tree_binning=False,
261
+ chi2_method=False,
262
+ seed=42,
263
+ chi2_p=0.999,
264
+ init_equi_bins=1000,
265
+ missing_rate_ref=-9999999,
266
+ spec_values=[],
267
+ base_metric="iv",
268
+ woe_engine="master",
269
+ woe_binner=None,
270
+ woe_engine_params=None,
271
+ ):
272
+ self._base = _BaseCorrelationFilter(
273
+ data, dep, corr_cutpoint, method, tree_binning, chi2_method, seed,
274
+ chi2_p, init_equi_bins, missing_rate_ref, spec_values, base_metric,
275
+ )
276
+ self.data = data
277
+ self.dep = dep
278
+ self.corr_cutpoint = corr_cutpoint
279
+ self.method = method
280
+ self.tree_binning = tree_binning
281
+ self.chi2_method = chi2_method
282
+ self.seed = seed
283
+ self.chi2_p = chi2_p
284
+ self.init_equi_bins = init_equi_bins
285
+ self.missing_rate_ref = missing_rate_ref
286
+ self.spec_values = spec_values
287
+ self.base_metric = base_metric
288
+ self.woe_engine = woe_engine
289
+ self.woe_binner = woe_binner
290
+ self.woe_engine_params = woe_engine_params or {}
291
+ self.correlated_dict = {}
292
+ self.filtered_varlist = []
293
+
294
+ def __getattr__(self, name):
295
+ return getattr(self._base, name)
296
+
297
+ @staticmethod
298
+ def calculate_vif(df):
299
+ return _BaseCorrelationFilter.calculate_vif(df)
300
+
301
+ def filter_single_iteration(self, varlist):
302
+ if self.woe_binner is None and self.woe_engine == "master":
303
+ return self._base.filter_single_iteration(varlist)
304
+
305
+ name_mapping = {"iv": "iv", "ks": "ks_in_gains"}
306
+ high_corr_var = var_corr_filter(self.data, varlist, corr_cutpoint=self.corr_cutpoint, method=self.method)
307
+ if len(high_corr_var) == 0:
308
+ return varlist
309
+
310
+ selected_varlist = []
311
+ removed_varlist = []
312
+ for var in tqdm(high_corr_var["VAR1"].drop_duplicates().tolist()):
313
+ if var in set(removed_varlist + selected_varlist):
314
+ continue
315
+ single_var_corr = high_corr_var.query(f"VAR1 == '{var}'")
316
+ correlated_list = [var] + single_var_corr["VAR2"].drop_duplicates().tolist()
317
+ insights = VarExtractionInsights(
318
+ self.data, self.dep, None, chi2_method=self.chi2_method,
319
+ chi2_p=self.chi2_p, init_equi_bins=self.init_equi_bins,
320
+ tree_binning=self.tree_binning, seed=self.seed,
321
+ missing_rate_ref=self.missing_rate_ref, spec_values=self.spec_values,
322
+ woe_engine=self.woe_engine, woe_binner=self.woe_binner,
323
+ woe_engine_params=self.woe_engine_params,
324
+ )
325
+ summary = insights.get_var_analysis_report(self.data, correlated_list, dep=self.dep, iv_cut=0)
326
+ if summary.empty:
327
+ continue
328
+ selected = summary.sort_values([name_mapping[self.base_metric.lower()]], ascending=False)["var"].iloc[0]
329
+ if selected not in selected_varlist:
330
+ selected_varlist.append(selected)
331
+ removed_varlist += [x for x in correlated_list if x != selected and x not in removed_varlist]
332
+ self.correlated_dict[var] = {"corr": single_var_corr, "gains": summary}
333
+
334
+ return selected_varlist + [x for x in varlist if x not in (selected_varlist + removed_varlist)]
335
+
336
+ def remove_highly_correlated(self, varlist, max_iterations=10):
337
+ if self.woe_binner is None and self.woe_engine == "master":
338
+ result = self._base.remove_highly_correlated(varlist, max_iterations)
339
+ self.correlated_dict = getattr(self._base, "correlated_dict", {})
340
+ self.filtered_varlist = getattr(self._base, "filtered_varlist", [])
341
+ return result
342
+
343
+ last_keep_list = self.filter_single_iteration(varlist)
344
+ for _ in range(1, max_iterations):
345
+ keep_list = self.filter_single_iteration(last_keep_list)
346
+ removed_vars = [x for x in last_keep_list if x not in keep_list]
347
+ self.filtered_varlist.append(removed_vars)
348
+ if len(removed_vars) == 0:
349
+ break
350
+ last_keep_list = keep_list
351
+ self.filtered_varlist = [x for x in varlist if x not in last_keep_list]
352
+ return last_keep_list
353
+
354
+
355
+ __all__ = ["PSICalculator", "VarExtractionInsights", "CorrelationFilter"]
@@ -0,0 +1,40 @@
1
+ from .Distribution_Tool import (
2
+ proc_means,
3
+ proc_means_by_grp,
4
+ get_distribution_shift,
5
+ get_distribution_shift_single_var,
6
+ plot_distribution,
7
+ DistributionShiftAnalyzer,
8
+ DistributionPlotter,
9
+ )
10
+
11
+ from .Feature_Insights import (
12
+ var_corr_filter,
13
+ )
14
+
15
+ from .PSI_Tool import (
16
+ calculate_psi,
17
+ calculate_within_psi,
18
+ calculate_psi_within_dataset,
19
+ calculate_multivar_psi_two_sets,
20
+ )
21
+
22
+ from .WOE_Engine_Feature_Patch import (
23
+ PSICalculator,
24
+ VarExtractionInsights,
25
+ CorrelationFilter,
26
+ )
27
+
28
+ __all__ = [
29
+ # Distribution_Tool
30
+ 'proc_means', 'proc_means_by_grp', 'get_distribution_shift',
31
+ 'get_distribution_shift_single_var', 'plot_distribution',
32
+ 'DistributionShiftAnalyzer', 'DistributionPlotter',
33
+
34
+ # Feature_Insights
35
+ 'VarExtractionInsights', 'var_corr_filter', 'CorrelationFilter',
36
+
37
+ # PSI_Tool
38
+ 'PSICalculator', 'calculate_psi', 'calculate_within_psi',
39
+ 'calculate_psi_within_dataset', 'calculate_multivar_psi_two_sets',
40
+ ]