SuperModelingFactory 0.6.5__tar.gz → 0.6.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Model_Eval_Tool.py +55 -8
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +1 -1
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/weighted_eval_utils.py +102 -13
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/_common.py +20 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/credit_model.py +111 -8
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/field_meta.py +6 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/__init__.py +1 -1
- {supermodelingfactory-0.6.5/SuperModelingFactory.egg-info → supermodelingfactory-0.6.6}/PKG-INFO +2 -2
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/README.md +1 -1
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6/SuperModelingFactory.egg-info}/PKG-INFO +2 -2
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/pyproject.toml +1 -1
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/setup.py +1 -1
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/ExcelMaster/ExcelFormatTool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/ExcelMaster/ExcelMaster.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/ExcelMaster/Template.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/ExcelMaster/Utility.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/ExcelMaster/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/LICENSE +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/MANIFEST.in +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Binning_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Proc_Compare.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Slope_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/kDataFrame.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/utils.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/evaluate_model.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Explainability/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Feature_Screen.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/ODPS_Distribution_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/ODPS_Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Weighted_Screen.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/Backward_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/GBM_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/LRM_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/feature_validation.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/screening_artifact.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Sample_Split.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/UAT/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Master.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/_utils/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/_utils/nan_guard.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/_utils/robust.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/_utils/sentinels.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/ref_font/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/ref_font/simsun.ttc +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Report/Report_Tool.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Report/__init__.py +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/SOURCES.txt +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/requires.txt +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/top_level.txt +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/requirements.txt +0 -0
- {supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/setup.cfg +0 -0
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Model_Eval_Tool.py
RENAMED
|
@@ -2169,7 +2169,7 @@ class PerformanceEvaluator:
|
|
|
2169
2169
|
dist_bins = 20, pct_bins = 10, precision = 5, min_bin_prop = 0.05,
|
|
2170
2170
|
include_missing = False, equal_freq = True, chi2_method = False,
|
|
2171
2171
|
init_equi_bins = 1000, chi2_p = 0.9, tree_binning = False, random_state = 42,
|
|
2172
|
-
weight_col = None):
|
|
2172
|
+
weight_col = None, spec_values = None, ascending = None):
|
|
2173
2173
|
"""
|
|
2174
2174
|
初始化性能评估器。
|
|
2175
2175
|
|
|
@@ -2225,9 +2225,29 @@ class PerformanceEvaluator:
|
|
|
2225
2225
|
self.tree_binning = tree_binning
|
|
2226
2226
|
self.random_state = random_state
|
|
2227
2227
|
self.weight_col = weight_col
|
|
2228
|
+
# spec_values: sentinel scores (e.g. -1 for all-missing overrides)
|
|
2229
|
+
# that get their own evaluation bin and are excluded from quantile
|
|
2230
|
+
# edges and ranking metrics. ascending: None keeps every underlying
|
|
2231
|
+
# function's own legacy default; an explicit bool is threaded through
|
|
2232
|
+
# summary/gains/plot and the weighted path uniformly.
|
|
2233
|
+
self.spec_values = list(spec_values) if spec_values else []
|
|
2234
|
+
self.ascending = ascending
|
|
2228
2235
|
self.datasets = {}
|
|
2229
2236
|
self.dataset_weight_cols = {}
|
|
2230
2237
|
self.evaluate_status = None
|
|
2238
|
+
|
|
2239
|
+
def _governed_eval_kwargs(self):
|
|
2240
|
+
"""Kwargs to thread spec_values/ascending into underlying eval calls.
|
|
2241
|
+
|
|
2242
|
+
Nothing is passed when unset, so every legacy default stays intact
|
|
2243
|
+
(unweighted summary False, comparison paths True, weighted False).
|
|
2244
|
+
"""
|
|
2245
|
+
kwargs = {}
|
|
2246
|
+
if self.spec_values:
|
|
2247
|
+
kwargs["spec_values"] = list(self.spec_values)
|
|
2248
|
+
if self.ascending is not None:
|
|
2249
|
+
kwargs["ascending"] = bool(self.ascending)
|
|
2250
|
+
return kwargs
|
|
2231
2251
|
|
|
2232
2252
|
def add_dataset(self, name, data, weight_col = None, overwrite = False):
|
|
2233
2253
|
"""
|
|
@@ -2351,6 +2371,8 @@ class PerformanceEvaluator:
|
|
|
2351
2371
|
scr_name,
|
|
2352
2372
|
weight_col=wc,
|
|
2353
2373
|
nbins=self.pct_bins,
|
|
2374
|
+
ascending=bool(self.ascending) if self.ascending is not None else False,
|
|
2375
|
+
spec_values=self.spec_values or None,
|
|
2354
2376
|
)
|
|
2355
2377
|
)
|
|
2356
2378
|
fnl_df = pd.DataFrame(rows)
|
|
@@ -2368,15 +2390,23 @@ class PerformanceEvaluator:
|
|
|
2368
2390
|
if scr_name is None:
|
|
2369
2391
|
scr_name = "_mdl_scr"
|
|
2370
2392
|
work_data[scr_name] = self.model.predict_proba(work_data.loc[:, self.feature_cols])[:, 1]
|
|
2371
|
-
payload = {
|
|
2372
|
-
"y_true": work_data[self.tgt_name],
|
|
2373
|
-
"y_score": work_data[scr_name],
|
|
2374
|
-
}
|
|
2375
2393
|
resolved_weight = _weighted_eval.resolve_weights(
|
|
2376
2394
|
work_data,
|
|
2377
2395
|
weight_col=wc,
|
|
2378
2396
|
expected_len=len(work_data),
|
|
2379
2397
|
)
|
|
2398
|
+
y_true = work_data[self.tgt_name].to_numpy()
|
|
2399
|
+
y_score = work_data[scr_name].to_numpy()
|
|
2400
|
+
if self.spec_values:
|
|
2401
|
+
keep = ~pd.Series(y_score).isin(self.spec_values).to_numpy()
|
|
2402
|
+
y_true = y_true[keep]
|
|
2403
|
+
y_score = y_score[keep]
|
|
2404
|
+
if resolved_weight is not None:
|
|
2405
|
+
resolved_weight = np.asarray(resolved_weight)[keep]
|
|
2406
|
+
payload = {
|
|
2407
|
+
"y_true": y_true,
|
|
2408
|
+
"y_score": y_score,
|
|
2409
|
+
}
|
|
2380
2410
|
if resolved_weight is not None:
|
|
2381
2411
|
payload["sample_weight"] = resolved_weight
|
|
2382
2412
|
eval_datasets[name] = payload
|
|
@@ -2445,6 +2475,21 @@ class PerformanceEvaluator:
|
|
|
2445
2475
|
return data[self.scr_name]
|
|
2446
2476
|
return self.model.predict_proba(data.loc[:, self.feature_cols])[:, 1]
|
|
2447
2477
|
|
|
2478
|
+
def _rank_payload(data):
|
|
2479
|
+
"""(y_true, y_score) with special sentinel scores removed.
|
|
2480
|
+
|
|
2481
|
+
Sentinels like the all-missing override -1 carry no ordering
|
|
2482
|
+
information: AUC/KS/distribution figures are computed on the
|
|
2483
|
+
non-special rows; their bin-level accounting lives in the gains
|
|
2484
|
+
table via spec_values.
|
|
2485
|
+
"""
|
|
2486
|
+
y_true = data[self.tgt_name]
|
|
2487
|
+
y_score = _get_score(data)
|
|
2488
|
+
if not self.spec_values:
|
|
2489
|
+
return y_true, y_score
|
|
2490
|
+
keep = ~pd.Series(np.asarray(y_score)).isin(self.spec_values).to_numpy()
|
|
2491
|
+
return np.asarray(y_true)[keep], np.asarray(y_score)[keep]
|
|
2492
|
+
|
|
2448
2493
|
def _get_benchmark_bin_edges():
|
|
2449
2494
|
if benchmark_dataset is None:
|
|
2450
2495
|
return None
|
|
@@ -2492,9 +2537,10 @@ class PerformanceEvaluator:
|
|
|
2492
2537
|
for name, data in dataset_dict.items():
|
|
2493
2538
|
if data is None:
|
|
2494
2539
|
continue
|
|
2540
|
+
y_true, y_score = _rank_payload(data)
|
|
2495
2541
|
eval_datasets[name] = {
|
|
2496
|
-
"y_true":
|
|
2497
|
-
"y_score":
|
|
2542
|
+
"y_true": y_true,
|
|
2543
|
+
"y_score": y_score
|
|
2498
2544
|
}
|
|
2499
2545
|
|
|
2500
2546
|
if len(eval_datasets) == 0:
|
|
@@ -2553,7 +2599,8 @@ class PerformanceEvaluator:
|
|
|
2553
2599
|
chi2_p = self.chi2_p,
|
|
2554
2600
|
retSummary = True,
|
|
2555
2601
|
tree_binning = False if benchmark_bin_edges is not None else self.tree_binning,
|
|
2556
|
-
random_state = self.random_state
|
|
2602
|
+
random_state = self.random_state,
|
|
2603
|
+
**self._governed_eval_kwargs(),
|
|
2557
2604
|
)
|
|
2558
2605
|
gains_res['index'] = name
|
|
2559
2606
|
gains_summ_list.append(gains_res)
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Model_Eval_Tool.pyi
RENAMED
|
@@ -32,6 +32,6 @@ class GainsTableCalculator:
|
|
|
32
32
|
def calculate(self, grp_name = None, min_data_size = 100, grp_colname = None, sync_range = True, retSummary = False, withSummary = False, wholeGroup = False, add_func = None): ...
|
|
33
33
|
|
|
34
34
|
class PerformanceEvaluator:
|
|
35
|
-
def __init__(self, tgt_name, scr_name = None, model = None, feature_cols = None, dist_bins = 20, pct_bins = 10, precision = 5, min_bin_prop = 0.05, include_missing = False, equal_freq = True, chi2_method = False, init_equi_bins = 1000, chi2_p = 0.9, tree_binning = False, random_state = 42, weight_col = None): ...
|
|
35
|
+
def __init__(self, tgt_name, scr_name = None, model = None, feature_cols = None, dist_bins = 20, pct_bins = 10, precision = 5, min_bin_prop = 0.05, include_missing = False, equal_freq = True, chi2_method = False, init_equi_bins = 1000, chi2_p = 0.9, tree_binning = False, random_state = 42, weight_col = None, spec_values = None, ascending = None): ...
|
|
36
36
|
def add_dataset(self, name, data, weight_col = None, overwrite = False): ...
|
|
37
37
|
def evaluate(self, oot_grp_name = None, min_data_size = 100, grp_colname = None, fig_save_path = None, rpt_save_path = None, to_show = False, display = True, gains_table = False, benchmark_dataset = None, weight_col = None): ...
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/weighted_eval_utils.py
RENAMED
|
@@ -95,10 +95,18 @@ def calc_pr(y_true, y_score, sample_weight=None):
|
|
|
95
95
|
)
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def rank_bins(score, weight, nbins):
|
|
98
|
+
def rank_bins(score, weight, nbins, ascending=False):
|
|
99
|
+
"""Weighted equal-frequency rank bins.
|
|
100
|
+
|
|
101
|
+
``ascending=False`` (default, legacy) sorts scores descending — bin 1 is
|
|
102
|
+
the highest-score bucket. ``ascending=True`` sorts scores ascending so
|
|
103
|
+
bin 1 is the lowest-score bucket, matching the unweighted
|
|
104
|
+
``gains_ascending=True`` convention.
|
|
105
|
+
"""
|
|
99
106
|
score = np.asarray(score, dtype=float)
|
|
100
107
|
weight = np.ones(len(score), dtype=float) if weight is None else np.asarray(weight, dtype=float)
|
|
101
|
-
|
|
108
|
+
sort_key = score if ascending else -score
|
|
109
|
+
order = np.lexsort((np.arange(len(score)), sort_key))
|
|
102
110
|
bins = np.empty(len(score), dtype=int)
|
|
103
111
|
total = float(weight.sum()) or float(len(score)) or 1.0
|
|
104
112
|
cum_weight = np.cumsum(weight[order])
|
|
@@ -108,18 +116,35 @@ def rank_bins(score, weight, nbins):
|
|
|
108
116
|
return bins
|
|
109
117
|
|
|
110
118
|
|
|
111
|
-
def
|
|
119
|
+
def _split_special_scores(df, score, spec_values):
|
|
120
|
+
"""Split a frame into (normal, special) rows by exact score membership.
|
|
121
|
+
|
|
122
|
+
Special score values (e.g. the all-missing override ``-1``) are business
|
|
123
|
+
sentinels, not model outputs: they must not participate in quantile
|
|
124
|
+
binning or ranking metrics.
|
|
125
|
+
"""
|
|
126
|
+
if not spec_values:
|
|
127
|
+
return df, None
|
|
128
|
+
spec_mask = df[score].isin(list(spec_values))
|
|
129
|
+
if not bool(spec_mask.any()):
|
|
130
|
+
return df, None
|
|
131
|
+
return df[~spec_mask], df[spec_mask]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_gains_table(data, dep, score, nbins=10, weight_col=None, weighted_binning=None,
|
|
135
|
+
ascending=False, spec_values=None, **kwargs):
|
|
112
136
|
cols = [dep, score]
|
|
113
137
|
if weight_col is not None and weight_col in data.columns:
|
|
114
138
|
cols.append(weight_col)
|
|
115
139
|
df = data[cols].copy()
|
|
140
|
+
df, special_df = _split_special_scores(df, score, spec_values)
|
|
116
141
|
weight = resolve_weights(df, weight_col=weight_col, expected_len=len(df))
|
|
117
142
|
if weight is None:
|
|
118
143
|
weight = np.ones(len(df), dtype=float)
|
|
119
144
|
|
|
120
145
|
y = df[dep].astype(float).to_numpy()
|
|
121
146
|
s = df[score].astype(float).to_numpy()
|
|
122
|
-
df["_bin_num"] = rank_bins(s, weight, nbins)
|
|
147
|
+
df["_bin_num"] = rank_bins(s, weight, nbins, ascending=ascending)
|
|
123
148
|
df["_bin_range"] = df["_bin_num"]
|
|
124
149
|
df["_w"] = weight
|
|
125
150
|
df["_bad_w"] = weight * y
|
|
@@ -165,13 +190,56 @@ def get_gains_table(data, dep, score, nbins=10, weight_col=None, weighted_binnin
|
|
|
165
190
|
out["KS_PER_BIN"] = (out["CUM_BAD_PCT"] - out["CUM_GOOD_PCT"]).abs()
|
|
166
191
|
out["KS"] = out["KS_PER_BIN"]
|
|
167
192
|
out["LIFT"] = out["AVG_BAD"] / overall_bad_rate if overall_bad_rate else np.nan
|
|
168
|
-
out["TRUE_BAD_SHIFT"] =
|
|
193
|
+
out["TRUE_BAD_SHIFT"] = (
|
|
194
|
+
(out["AVG_BAD"].shift(1) / out["AVG_BAD"] - 1)
|
|
195
|
+
if not ascending
|
|
196
|
+
else (out["AVG_BAD"] / out["AVG_BAD"].shift(1) - 1)
|
|
197
|
+
)
|
|
169
198
|
out["RANK_ORDER_BUMP"] = out["TRUE_BAD_SHIFT"].lt(0).astype(int)
|
|
170
199
|
with np.errstate(divide="ignore", invalid="ignore"):
|
|
171
200
|
out["WOE"] = np.log(out["BAD_PCT_IN_EACH_BIN"] / out["GOOD_PCT_IN_EACH_BIN"])
|
|
172
201
|
out["WOE"] = out["WOE"].replace([np.inf, -np.inf], 0).fillna(0)
|
|
173
202
|
out["IV"] = (out["BAD_PCT_IN_EACH_BIN"] - out["GOOD_PCT_IN_EACH_BIN"]) * out["WOE"]
|
|
174
203
|
out["AUC"] = safe_auc(y, s, sample_weight=weight)
|
|
204
|
+
|
|
205
|
+
if special_df is not None and len(special_df):
|
|
206
|
+
# Special sentinel scores (e.g. -1 for all-missing rows) get their own
|
|
207
|
+
# descriptive rows: never part of quantile edges, cumulative columns,
|
|
208
|
+
# or ranking metrics (those stay NaN by construction).
|
|
209
|
+
spec_weight = resolve_weights(special_df, weight_col=weight_col, expected_len=len(special_df))
|
|
210
|
+
if spec_weight is None:
|
|
211
|
+
spec_weight = np.ones(len(special_df), dtype=float)
|
|
212
|
+
grand_total = total_weight + float(np.sum(spec_weight))
|
|
213
|
+
spec_rows = []
|
|
214
|
+
for value, part in special_df.groupby(score, sort=True):
|
|
215
|
+
part_w = resolve_weights(part, weight_col=weight_col, expected_len=len(part))
|
|
216
|
+
if part_w is None:
|
|
217
|
+
part_w = np.ones(len(part), dtype=float)
|
|
218
|
+
part_y = part[dep].astype(float).to_numpy()
|
|
219
|
+
n_w = float(np.sum(part_w))
|
|
220
|
+
n_bad = float(np.sum(part_w * part_y))
|
|
221
|
+
label = f"special:{value}"
|
|
222
|
+
spec_rows.append(
|
|
223
|
+
pd.DataFrame(
|
|
224
|
+
{
|
|
225
|
+
"MIN": [value],
|
|
226
|
+
"MAX": [value],
|
|
227
|
+
"N": [n_w],
|
|
228
|
+
"N_RAW": [int(len(part))],
|
|
229
|
+
"PERF_CNT": [n_w],
|
|
230
|
+
"N_BAD": [n_bad],
|
|
231
|
+
"N_GOOD": [n_w - n_bad],
|
|
232
|
+
"AVG_SCORE": [float(value)],
|
|
233
|
+
"UNIQUE_SCORE": [1],
|
|
234
|
+
"PROP": [n_w / grand_total if grand_total else np.nan],
|
|
235
|
+
"AVG_BAD": [n_bad / n_w if n_w else np.nan],
|
|
236
|
+
"AVG_GOOD": [(n_w - n_bad) / n_w if n_w else np.nan],
|
|
237
|
+
},
|
|
238
|
+
index=pd.MultiIndex.from_tuples([(label, label)], names=out.index.names),
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
out = pd.concat([out] + spec_rows)
|
|
242
|
+
out["AUC"] = out["AUC"].iloc[0]
|
|
175
243
|
return out
|
|
176
244
|
|
|
177
245
|
|
|
@@ -212,32 +280,53 @@ def calc_fixed_pct(y_true, y_score, sample_weight=None, **kwargs):
|
|
|
212
280
|
return calc_equid_dist(y_true, y_score, sample_weight=sample_weight, **kwargs)
|
|
213
281
|
|
|
214
282
|
|
|
215
|
-
def dataset_summary(name, data, tgt_name, scr_name, weight_col=None, nbins=10
|
|
283
|
+
def dataset_summary(name, data, tgt_name, scr_name, weight_col=None, nbins=10,
|
|
284
|
+
ascending=False, spec_values=None):
|
|
216
285
|
weight = resolve_weights(data, weight_col=weight_col, expected_len=len(data))
|
|
217
286
|
y_true = data[tgt_name].to_numpy()
|
|
218
287
|
y_score = data[scr_name].to_numpy()
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
288
|
+
# Ranking metrics (AUC/KS/avgScore) are computed on non-special rows only:
|
|
289
|
+
# sentinel scores like -1 carry no ordering information.
|
|
290
|
+
rank_data, special_part = _split_special_scores(data, scr_name, spec_values)
|
|
291
|
+
rank_weight = resolve_weights(rank_data, weight_col=weight_col, expected_len=len(rank_data))
|
|
292
|
+
rank_true = rank_data[tgt_name].to_numpy()
|
|
293
|
+
rank_score = rank_data[scr_name].to_numpy()
|
|
294
|
+
roc_df = calc_roc(rank_true, rank_score, sample_weight=rank_weight)
|
|
295
|
+
gains = get_gains_table(
|
|
296
|
+
data, tgt_name, scr_name, nbins=nbins, weight_col=weight_col,
|
|
297
|
+
ascending=ascending, spec_values=spec_values,
|
|
298
|
+
)
|
|
299
|
+
summary = {
|
|
222
300
|
"index": name,
|
|
223
301
|
"dataset": name,
|
|
224
302
|
"DATASET": name,
|
|
225
|
-
"AUC": safe_auc(
|
|
303
|
+
"AUC": safe_auc(rank_true, rank_score, sample_weight=rank_weight),
|
|
226
304
|
"KS": float(roc_df["KS"].max()) if "KS" in roc_df else np.nan,
|
|
227
305
|
"LIFT": float(gains["LIFT"].max()) if "LIFT" in gains else np.nan,
|
|
228
306
|
"IV": float(gains["IV"].sum()) if "IV" in gains else np.nan,
|
|
229
307
|
"N": float(np.sum(weight)) if weight is not None else float(len(data)),
|
|
230
308
|
"N_RAW": int(len(data)),
|
|
231
309
|
"avgTrue": safe_weighted_average(y_true, weight),
|
|
232
|
-
"avgScore": safe_weighted_average(
|
|
310
|
+
"avgScore": safe_weighted_average(rank_score, rank_weight),
|
|
233
311
|
}
|
|
312
|
+
if special_part is not None:
|
|
313
|
+
spec_weight = resolve_weights(special_part, weight_col=weight_col, expected_len=len(special_part))
|
|
314
|
+
summary["N_SPECIAL"] = (
|
|
315
|
+
float(np.sum(spec_weight)) if spec_weight is not None else float(len(special_part))
|
|
316
|
+
)
|
|
317
|
+
summary["N_SPECIAL_RAW"] = int(len(special_part))
|
|
318
|
+
return summary
|
|
234
319
|
|
|
235
320
|
|
|
236
|
-
def get_perf_summary(train=None, validation=None, oot=None, tgt_name=None, scr_name=None, weight_col=None, nbins=10,
|
|
321
|
+
def get_perf_summary(train=None, validation=None, oot=None, tgt_name=None, scr_name=None, weight_col=None, nbins=10,
|
|
322
|
+
ascending=False, spec_values=None, **kwargs):
|
|
237
323
|
rows = []
|
|
238
324
|
for name, data in (("ins", train), ("oos", validation), ("oot", oot)):
|
|
239
325
|
if data is not None:
|
|
240
|
-
rows.append(dataset_summary(
|
|
326
|
+
rows.append(dataset_summary(
|
|
327
|
+
name, data, tgt_name, scr_name, weight_col=weight_col, nbins=nbins,
|
|
328
|
+
ascending=ascending, spec_values=spec_values,
|
|
329
|
+
))
|
|
241
330
|
return pd.DataFrame(rows)
|
|
242
331
|
|
|
243
332
|
|
|
@@ -532,6 +532,26 @@ def copy_column_length_checked(
|
|
|
532
532
|
dst[col] = src[col].to_numpy()
|
|
533
533
|
|
|
534
534
|
|
|
535
|
+
def all_missing_mask(data: pd.DataFrame, feature_cols: list[str]) -> np.ndarray:
|
|
536
|
+
"""Rows where every listed feature is missing.
|
|
537
|
+
|
|
538
|
+
Same membership test as ``Core.scoring``'s all-missing branch
|
|
539
|
+
(``pd.isnull(data[varlist]).sum(axis=1) == len(varlist)``), so the
|
|
540
|
+
pipeline's evaluation override and the production scoring API agree on
|
|
541
|
+
which rows count as un-scorable.
|
|
542
|
+
"""
|
|
543
|
+
features = list(feature_cols or [])
|
|
544
|
+
if not features:
|
|
545
|
+
return np.zeros(len(data), dtype=bool)
|
|
546
|
+
absent = [col for col in features if col not in data.columns]
|
|
547
|
+
if absent:
|
|
548
|
+
raise KeyError(
|
|
549
|
+
f"all-missing score override needs raw feature columns "
|
|
550
|
+
f"{absent[:5]} which are absent from the evaluation frame."
|
|
551
|
+
)
|
|
552
|
+
return (pd.isnull(data[features]).sum(axis=1) == len(features)).to_numpy()
|
|
553
|
+
|
|
554
|
+
|
|
535
555
|
def resolve_missing_oot(
|
|
536
556
|
oos: pd.DataFrame,
|
|
537
557
|
synthesize: bool,
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/credit_model.py
RENAMED
|
@@ -10,6 +10,7 @@ import warnings
|
|
|
10
10
|
|
|
11
11
|
from ._common import (
|
|
12
12
|
add_dataset_with_optional_weight,
|
|
13
|
+
all_missing_mask,
|
|
13
14
|
apply_woe_fit_query,
|
|
14
15
|
as_list,
|
|
15
16
|
copy_column_length_checked,
|
|
@@ -121,6 +122,28 @@ class CreditModelPipelineConfig:
|
|
|
121
122
|
perf_pct_bins: int = 10
|
|
122
123
|
perf_min_bin_prop: float = 0.03
|
|
123
124
|
|
|
125
|
+
# --- Evaluation governance (G13/G14/G15/G16) -------------------------
|
|
126
|
+
# eval_target_cols: extra labels evaluated against the SAME frozen model
|
|
127
|
+
# scores; effective label set = [target_col] + eval_target_cols (deduped).
|
|
128
|
+
# all_missing_score_value: rows whose raw model features are ALL missing
|
|
129
|
+
# get this score (e.g. -1); same test as Core.scoring's
|
|
130
|
+
# all_missing_spec_value, persisted in the model artifact metadata.
|
|
131
|
+
# special_score_values: sentinel scores (e.g. [-1]) get their own
|
|
132
|
+
# evaluation bin, excluded from quantile edges and ranking metrics.
|
|
133
|
+
# gains_ascending: None keeps each underlying path's legacy default;
|
|
134
|
+
# an explicit bool is threaded through summary/gains/weighted uniformly.
|
|
135
|
+
# NOTE (announced flip): the next minor release flips the resolved
|
|
136
|
+
# default of gains_ascending to True (score ascending, bin 1 = low risk).
|
|
137
|
+
# eval_weight_col: "inherit" (default) reuses weight_col for evaluation
|
|
138
|
+
# and search/backward eval weights; None evaluates unweighted even when
|
|
139
|
+
# training is weighted; any other string names the evaluation weight
|
|
140
|
+
# column explicitly.
|
|
141
|
+
eval_target_cols: list[str] | None = None
|
|
142
|
+
all_missing_score_value: float | None = None
|
|
143
|
+
special_score_values: list[float] | None = None
|
|
144
|
+
gains_ascending: bool | None = None
|
|
145
|
+
eval_weight_col: str | None = "inherit"
|
|
146
|
+
|
|
124
147
|
screening_artifact: Any | None = None
|
|
125
148
|
feature_validation_result: Any | None = None
|
|
126
149
|
feature_selection_mode: Literal["run", "from_artifact", "skip"] = "run"
|
|
@@ -373,6 +396,20 @@ class CreditModelPipeline:
|
|
|
373
396
|
if cfg.woe_fit_query:
|
|
374
397
|
validate_woe_fit_query_columns(cfg.woe_fit_query, data.columns, context="input data")
|
|
375
398
|
validate_woe_fit_query_syntax(data, cfg.woe_fit_query)
|
|
399
|
+
eval_targets = self._effective_eval_targets()
|
|
400
|
+
missing_targets = [t for t in eval_targets if t not in data.columns]
|
|
401
|
+
if missing_targets:
|
|
402
|
+
raise KeyError(
|
|
403
|
+
f"Missing evaluation target column(s) {missing_targets} "
|
|
404
|
+
f"(target_col + eval_target_cols must all exist in the input data)"
|
|
405
|
+
)
|
|
406
|
+
eval_weight = self._resolve_eval_weight_col()
|
|
407
|
+
if eval_weight and eval_weight != cfg.weight_col and eval_weight not in data.columns:
|
|
408
|
+
raise KeyError(f"Missing eval_weight_col {eval_weight!r}")
|
|
409
|
+
if cfg.all_missing_score_value is not None:
|
|
410
|
+
float(cfg.all_missing_score_value)
|
|
411
|
+
if cfg.special_score_values is not None:
|
|
412
|
+
[float(v) for v in cfg.special_score_values]
|
|
376
413
|
if cfg.extra_eval_datasets:
|
|
377
414
|
reserved = {"ins", "oos", "oot"}
|
|
378
415
|
conflicts = sorted(set(cfg.extra_eval_datasets) & reserved)
|
|
@@ -381,8 +418,11 @@ class CreditModelPipeline:
|
|
|
381
418
|
f"extra_eval_datasets names cannot conflict with split names {sorted(reserved)}: {conflicts}"
|
|
382
419
|
)
|
|
383
420
|
for name, extra_df in cfg.extra_eval_datasets.items():
|
|
384
|
-
if
|
|
385
|
-
|
|
421
|
+
extra_missing = [t for t in eval_targets if t not in extra_df.columns]
|
|
422
|
+
if extra_missing:
|
|
423
|
+
raise KeyError(
|
|
424
|
+
f"extra_eval_datasets[{name!r}] missing evaluation target column(s) {extra_missing}"
|
|
425
|
+
)
|
|
386
426
|
if cfg.warm_start_enabled and cfg.warm_start_score_col and cfg.warm_start_score_col not in extra_df.columns:
|
|
387
427
|
raise KeyError(
|
|
388
428
|
f"extra_eval_datasets[{name!r}] missing warm_start_score_col {cfg.warm_start_score_col!r}"
|
|
@@ -475,6 +515,28 @@ class CreditModelPipeline:
|
|
|
475
515
|
"oot_withheld": False,
|
|
476
516
|
}
|
|
477
517
|
|
|
518
|
+
def _resolve_eval_weight_col(self) -> str | None:
|
|
519
|
+
"""Resolve the evaluation weight column (G16).
|
|
520
|
+
|
|
521
|
+
"inherit" (default) reuses the training weight_col — the legacy
|
|
522
|
+
behavior; None evaluates unweighted even when training is weighted;
|
|
523
|
+
any other string names the evaluation weight column explicitly.
|
|
524
|
+
"""
|
|
525
|
+
value = self.config.eval_weight_col
|
|
526
|
+
if value == "inherit":
|
|
527
|
+
return self.config.weight_col
|
|
528
|
+
return value
|
|
529
|
+
|
|
530
|
+
def _effective_eval_targets(self) -> list[str]:
|
|
531
|
+
"""[target_col] + eval_target_cols, deduplicated, order preserved."""
|
|
532
|
+
cfg = self.config
|
|
533
|
+
targets = [cfg.target_col]
|
|
534
|
+
for name in as_list(cfg.eval_target_cols):
|
|
535
|
+
label = str(name)
|
|
536
|
+
if label not in targets:
|
|
537
|
+
targets.append(label)
|
|
538
|
+
return targets
|
|
539
|
+
|
|
478
540
|
def _apply_split_governance(self, splits: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]:
|
|
479
541
|
"""Remove forbidden splits from the working splits dict entirely, so
|
|
480
542
|
no downstream stage can consume them even by accident."""
|
|
@@ -925,7 +987,16 @@ class CreditModelPipeline:
|
|
|
925
987
|
features = list(selected_raw_features if source == "raw" else selected_woe_features)
|
|
926
988
|
else:
|
|
927
989
|
continue
|
|
928
|
-
inputs[name] = {
|
|
990
|
+
inputs[name] = {
|
|
991
|
+
"source": source,
|
|
992
|
+
"splits": splits,
|
|
993
|
+
"features": features,
|
|
994
|
+
# Raw-column view of the model's inputs, used by the
|
|
995
|
+
# all-missing score override (G14): the business rule is
|
|
996
|
+
# defined on raw features regardless of what representation
|
|
997
|
+
# the model consumed.
|
|
998
|
+
"raw_features": list(selected_raw_features if source == "woe" else features),
|
|
999
|
+
}
|
|
929
1000
|
return inputs
|
|
930
1001
|
|
|
931
1002
|
@staticmethod
|
|
@@ -1051,7 +1122,7 @@ class CreditModelPipeline:
|
|
|
1051
1122
|
"validation_data": splits[validation_split],
|
|
1052
1123
|
"test_data_dict": test_data_dict,
|
|
1053
1124
|
"weight_col": cfg.weight_col,
|
|
1054
|
-
"validation_weight_col":
|
|
1125
|
+
"validation_weight_col": self._resolve_eval_weight_col(),
|
|
1055
1126
|
},
|
|
1056
1127
|
user_init,
|
|
1057
1128
|
)
|
|
@@ -1196,7 +1267,7 @@ class CreditModelPipeline:
|
|
|
1196
1267
|
eval_sets=eval_sets,
|
|
1197
1268
|
param_grid=cfg.lr_search_param_grid,
|
|
1198
1269
|
weight_col=cfg.weight_col,
|
|
1199
|
-
eval_weight_col=
|
|
1270
|
+
eval_weight_col=self._resolve_eval_weight_col(),
|
|
1200
1271
|
**params,
|
|
1201
1272
|
)
|
|
1202
1273
|
self._lr_best_params = dict(getattr(lr, "best_params_", {}) or {})
|
|
@@ -1332,7 +1403,7 @@ class CreditModelPipeline:
|
|
|
1332
1403
|
search_space=search_spaces[name],
|
|
1333
1404
|
fit_kwargs=fit_kwargs or None,
|
|
1334
1405
|
weight_col=cfg.weight_col,
|
|
1335
|
-
eval_weight_col=
|
|
1406
|
+
eval_weight_col=self._resolve_eval_weight_col(),
|
|
1336
1407
|
**common,
|
|
1337
1408
|
)
|
|
1338
1409
|
except Exception as exc:
|
|
@@ -1357,12 +1428,16 @@ class CreditModelPipeline:
|
|
|
1357
1428
|
model_extra = {key: df.copy() for key, df in cfg.extra_eval_datasets.items()}
|
|
1358
1429
|
else:
|
|
1359
1430
|
model_extra = extra_eval_splits
|
|
1431
|
+
eval_targets = self._effective_eval_targets()
|
|
1432
|
+
eval_weight = self._resolve_eval_weight_col()
|
|
1360
1433
|
evaluator = PerformanceEvaluator(
|
|
1361
|
-
tgt_name=cfg.target_col,
|
|
1434
|
+
tgt_name=eval_targets if len(eval_targets) > 1 else cfg.target_col,
|
|
1362
1435
|
scr_name=f"pred_{name}",
|
|
1363
1436
|
pct_bins=cfg.perf_pct_bins,
|
|
1364
1437
|
min_bin_prop=cfg.perf_min_bin_prop,
|
|
1365
1438
|
equal_freq=True,
|
|
1439
|
+
spec_values=cfg.special_score_values,
|
|
1440
|
+
ascending=cfg.gains_ascending,
|
|
1366
1441
|
)
|
|
1367
1442
|
allowed = self._governance.get("evaluation_splits")
|
|
1368
1443
|
if allowed is not None:
|
|
@@ -1372,8 +1447,12 @@ class CreditModelPipeline:
|
|
|
1372
1447
|
for ds_name, df in eval_splits.items():
|
|
1373
1448
|
scored = df.copy()
|
|
1374
1449
|
scored[f"pred_{name}"] = self._predict_model_positive(name, wrapper, scored, feature_cols)
|
|
1450
|
+
if cfg.all_missing_score_value is not None:
|
|
1451
|
+
override = all_missing_mask(scored, model_inputs[name].get("raw_features") or [])
|
|
1452
|
+
if override.any():
|
|
1453
|
+
scored.loc[override, f"pred_{name}"] = float(cfg.all_missing_score_value)
|
|
1375
1454
|
nan_stats[str(ds_name)] = int((~np.isfinite(scored[f"pred_{name}"].to_numpy(dtype=float))).sum())
|
|
1376
|
-
add_dataset_with_optional_weight(evaluator, ds_name, scored, weight_col=
|
|
1455
|
+
add_dataset_with_optional_weight(evaluator, ds_name, scored, weight_col=eval_weight)
|
|
1377
1456
|
self.predict_positive_nan_stats[str(name)] = nan_stats
|
|
1378
1457
|
total_bad = sum(nan_stats.values())
|
|
1379
1458
|
if total_bad:
|
|
@@ -1391,6 +1470,21 @@ class CreditModelPipeline:
|
|
|
1391
1470
|
results[name] = evaluator.evaluate(to_show=False, display=False, fig_save_path=fig_save_path)
|
|
1392
1471
|
return results
|
|
1393
1472
|
|
|
1473
|
+
def _raw_features_for_saved_model(
|
|
1474
|
+
self,
|
|
1475
|
+
model_name: str,
|
|
1476
|
+
model_feature_sources: dict[str, str],
|
|
1477
|
+
model_feature_sets: dict[str, list[str]],
|
|
1478
|
+
woe_artifacts: dict[str, Any],
|
|
1479
|
+
) -> list[str]:
|
|
1480
|
+
"""Raw-column view of a saved model's inputs, for the all-missing
|
|
1481
|
+
override metadata (the business rule is defined on raw features)."""
|
|
1482
|
+
features = list(model_feature_sets.get(model_name, []))
|
|
1483
|
+
if str(model_feature_sources.get(model_name, "woe")).lower() == "raw":
|
|
1484
|
+
return features
|
|
1485
|
+
suffix = woe_artifacts.get("woe_suffix", self.config.woe_params.get("woe_suffix", "_woe"))
|
|
1486
|
+
return self._woe_to_raw_features(features, suffix)
|
|
1487
|
+
|
|
1394
1488
|
def _predict_model_positive(
|
|
1395
1489
|
self,
|
|
1396
1490
|
model_name: str,
|
|
@@ -1605,6 +1699,15 @@ class CreditModelPipeline:
|
|
|
1605
1699
|
"oot_withheld": self._governance["oot_withheld"],
|
|
1606
1700
|
"evaluation_splits": self._governance["evaluation_splits"],
|
|
1607
1701
|
"forbidden_splits": self._governance["forbidden_splits"],
|
|
1702
|
+
"eval_target_cols": list(cfg.eval_target_cols) if cfg.eval_target_cols else None,
|
|
1703
|
+
"all_missing_score_value": cfg.all_missing_score_value,
|
|
1704
|
+
"all_missing_raw_features": (
|
|
1705
|
+
self._raw_features_for_saved_model(name, model_feature_sources, model_feature_sets, woe_artifacts)
|
|
1706
|
+
if cfg.all_missing_score_value is not None
|
|
1707
|
+
else None
|
|
1708
|
+
),
|
|
1709
|
+
"special_score_values": list(cfg.special_score_values) if cfg.special_score_values else None,
|
|
1710
|
+
"gains_ascending": cfg.gains_ascending,
|
|
1608
1711
|
}
|
|
1609
1712
|
metrics = None
|
|
1610
1713
|
if isinstance(perf_results.get(name), pd.DataFrame):
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/field_meta.py
RENAMED
|
@@ -178,6 +178,11 @@ _FIELD_LABELS = {
|
|
|
178
178
|
"backward_params": "逐步回归参数",
|
|
179
179
|
"use_backward_features": "使用逐步回归特征",
|
|
180
180
|
"candidate_mode": "候选模式(禁用 OOT)",
|
|
181
|
+
"eval_target_cols": "额外评估标签",
|
|
182
|
+
"all_missing_score_value": "全缺失分数覆写值",
|
|
183
|
+
"special_score_values": "特殊分数独立箱",
|
|
184
|
+
"gains_ascending": "Gains 分数升序",
|
|
185
|
+
"eval_weight_col": "评估权重列",
|
|
181
186
|
"synthesize_missing_oot": "缺 OOT 时用 OOS 副本代替",
|
|
182
187
|
"evaluation_splits": "评估 split 白名单",
|
|
183
188
|
"forbidden_splits": "禁用 split(硬闸)",
|
|
@@ -364,6 +369,7 @@ _FIELD_OPTIONS = {
|
|
|
364
369
|
"feature_selection_mode": ["run", "from_artifact", "skip"],
|
|
365
370
|
"search_objective_when_no_oot": ["max_primary", "oot_gap_penalized"],
|
|
366
371
|
"backward_validation_split": ["ins", "oos", "oot"],
|
|
372
|
+
"gains_ascending": [True, False],
|
|
367
373
|
"prescore_model_type": ["lgb", "xgb", "cat", "lr"],
|
|
368
374
|
"ri_model_type": ["lgb", "xgb", "cat", "lr"],
|
|
369
375
|
"lr_nan_handling": ["fillna_median", "fillna_mean", "fillna_0", "raise"],
|
{supermodelingfactory-0.6.5/SuperModelingFactory.egg-info → supermodelingfactory-0.6.6}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SuperModelingFactory
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.6
|
|
4
4
|
Summary: Credit risk modeling factory: WOE binning, scorecards, LightGBM, Excel reporting.
|
|
5
5
|
Home-page: https://github.com/Kyle-J-Sun/SuperModelingFactory
|
|
6
6
|
Author: Kyle Sun
|
|
@@ -265,7 +265,7 @@ em.close_workbook()
|
|
|
265
265
|
|
|
266
266
|
## 版本
|
|
267
267
|
|
|
268
|
-
- **Version**: 0.6.
|
|
268
|
+
- **Version**: 0.6.6
|
|
269
269
|
- **Author**: Jingkai Sun
|
|
270
270
|
|
|
271
271
|
## 许可证
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6/SuperModelingFactory.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SuperModelingFactory
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.6
|
|
4
4
|
Summary: Credit risk modeling factory: WOE binning, scorecards, LightGBM, Excel reporting.
|
|
5
5
|
Home-page: https://github.com/Kyle-J-Sun/SuperModelingFactory
|
|
6
6
|
Author: Kyle Sun
|
|
@@ -265,7 +265,7 @@ em.close_workbook()
|
|
|
265
265
|
|
|
266
266
|
## 版本
|
|
267
267
|
|
|
268
|
-
- **Version**: 0.6.
|
|
268
|
+
- **Version**: 0.6.6
|
|
269
269
|
- **Author**: Jingkai Sun
|
|
270
270
|
|
|
271
271
|
## 许可证
|
|
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
|
|
|
12
12
|
|
|
13
13
|
[project]
|
|
14
14
|
name = "SuperModelingFactory"
|
|
15
|
-
version = "0.6.
|
|
15
|
+
version = "0.6.6"
|
|
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.6.
|
|
18
|
+
version=os.environ.get("SMF_VERSION", "0.6.6"),
|
|
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.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Binning_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Binning_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Json_Data_Converter.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Model_Registry_Tool.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Parallel_Engine.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/Proc_Compare.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/XOR_Encryptor.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/XOR_Encryptor.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Core/sample_weight_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Evaluation_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/Evaluation_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/evaluate_model.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Eval/evaluate_model.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Explainability/__init__.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Distribution_Tool.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Feature_Insights.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Feature_Insights.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Feature_Screen.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/PSI_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Feature/Weighted_Screen.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/Backward_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/Backward_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Model/GBM_Search_Tool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/mock_sample.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/orchestrator.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/reject_inference.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/sample_analysis.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Pipeline/score_comparison.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Reject_Infer.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Reject_Infer.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Sample_Split.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/Sample/Sample_Split.pyi
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
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Plot_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Report_Builder.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/WOE_Report_Builder.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/plot_woe_tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/WOE/plot_woe_tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/Modeling_Tool/ref_font/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{supermodelingfactory-0.6.5 → supermodelingfactory-0.6.6}/SuperModelingFactory.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|