SuperModelingFactory 0.5.2__tar.gz → 0.5.4__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.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Binning_Tool.py +1 -2
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/ODPS_Tool.py +97 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/sample_weight_utils.py +6 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/Model_Eval_Tool.py +43 -4
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +3 -3
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/weighted_eval_utils.py +3 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/LRM_Tool.py +45 -6
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/LRM_Tool.pyi +2 -2
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/__init__.py +32 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/_common.py +36 -2
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/credit_model.py +16 -1
- supermodelingfactory-0.5.4/Modeling_Tool/Pipeline/field_meta.py +1117 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/reject_inference.py +16 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -2
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Sample/Reject_Infer.py +50 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Sample/Reject_Infer.pyi +1 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/__init__.py +20 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/_utils/__init__.py +2 -1
- supermodelingfactory-0.5.4/Modeling_Tool/_utils/nan_guard.py +31 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/PKG-INFO +2 -2
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/README.md +1 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/SuperModelingFactory.egg-info/PKG-INFO +2 -2
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/SuperModelingFactory.egg-info/SOURCES.txt +2 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/pyproject.toml +1 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/setup.py +1 -1
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/ExcelMaster/ExcelFormatTool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/ExcelMaster/ExcelMaster.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/ExcelMaster/Template.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/ExcelMaster/Utility.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/ExcelMaster/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/LICENSE +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/MANIFEST.in +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Proc_Compare.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Slope_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/kDataFrame.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/utils.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/evaluate_model.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Explainability/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/Feature_Screen.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/Weighted_Screen.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Feature/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/Backward_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/GBM_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Model/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/feature_validation.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/screening_artifact.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Sample/Sample_Split.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Sample/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/UAT/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Master.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/_utils/robust.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/_utils/sentinels.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/ref_font/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/ref_font/simsun.ttc +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Report/Report_Tool.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Report/__init__.py +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/SuperModelingFactory.egg-info/requires.txt +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/SuperModelingFactory.egg-info/top_level.txt +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/requirements.txt +0 -0
- {supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/setup.cfg +0 -0
{supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/Binning_Tool.py
RENAMED
|
@@ -367,8 +367,7 @@ class NumVarBinning:
|
|
|
367
367
|
if modify:
|
|
368
368
|
self.cut_points = self.modify_cutpoints(df, points)
|
|
369
369
|
else:
|
|
370
|
-
|
|
371
|
-
self.cut_points = points
|
|
370
|
+
self.cut_points = sorted(points)
|
|
372
371
|
self.bins = len(self.cut_points) - 1
|
|
373
372
|
|
|
374
373
|
binning_series = pd.cut(df[self.var_name], self.cut_points, right=False)
|
|
@@ -39,6 +39,41 @@ def _make_related_odps_table_name(table_name, suffix):
|
|
|
39
39
|
return related_bare, related_bare
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
def _parse_odps_partition_spec(partition):
|
|
43
|
+
if isinstance(partition, dict):
|
|
44
|
+
return [(str(k).strip(), str(v).strip().strip("'\"")) for k, v in partition.items()]
|
|
45
|
+
pairs = []
|
|
46
|
+
for item in str(partition).split(","):
|
|
47
|
+
if not item.strip():
|
|
48
|
+
continue
|
|
49
|
+
if "=" not in item:
|
|
50
|
+
raise ValueError(f"Invalid ODPS partition spec item {item!r}; expected key=value.")
|
|
51
|
+
key, value = item.split("=", 1)
|
|
52
|
+
pairs.append((key.strip(), value.strip().strip("'\"")))
|
|
53
|
+
if not pairs:
|
|
54
|
+
raise ValueError("partition must contain at least one key=value pair.")
|
|
55
|
+
return pairs
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _format_odps_partition_spec(pairs, quoted=False):
|
|
59
|
+
parts = []
|
|
60
|
+
for key, value in pairs:
|
|
61
|
+
safe_value = str(value).replace("'", "''")
|
|
62
|
+
if quoted:
|
|
63
|
+
parts.append(f"{key}='{safe_value}'")
|
|
64
|
+
else:
|
|
65
|
+
parts.append(f"{key}={safe_value}")
|
|
66
|
+
return ",".join(parts)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _make_related_odps_partition_spec(partition, suffix):
|
|
70
|
+
pairs = _parse_odps_partition_spec(partition)
|
|
71
|
+
related = list(pairs)
|
|
72
|
+
key, value = related[-1]
|
|
73
|
+
related[-1] = (key, f"{value}{suffix}")
|
|
74
|
+
return _format_odps_partition_spec(related, quoted=False)
|
|
75
|
+
|
|
76
|
+
|
|
42
77
|
class ODPSRunner(object):
|
|
43
78
|
_wide_schema_patch_lock = threading.RLock()
|
|
44
79
|
_wide_schema_patch_ref_count = 0
|
|
@@ -392,7 +427,21 @@ class ODPSRunner(object):
|
|
|
392
427
|
|
|
393
428
|
logger.info(f'<<<< 完成数据入表{table_name}: shape={df.shape} >>>>')
|
|
394
429
|
|
|
395
|
-
def
|
|
430
|
+
def _partition_exists(self, table, partition):
|
|
431
|
+
if hasattr(table, "exist_partition"):
|
|
432
|
+
return bool(table.exist_partition(partition))
|
|
433
|
+
try:
|
|
434
|
+
table.get_partition(partition)
|
|
435
|
+
return True
|
|
436
|
+
except Exception:
|
|
437
|
+
return False
|
|
438
|
+
|
|
439
|
+
def _rename_partition(self, table_name, source_partition, target_partition):
|
|
440
|
+
src = _format_odps_partition_spec(_parse_odps_partition_spec(source_partition), quoted=True)
|
|
441
|
+
dst = _format_odps_partition_spec(_parse_odps_partition_spec(target_partition), quoted=True)
|
|
442
|
+
self.o.run_sql(f"ALTER TABLE {table_name} PARTITION ({src}) RENAME TO PARTITION ({dst});")
|
|
443
|
+
|
|
444
|
+
def insert_df(self, df, table_name, overwrite=True, partition=None, atomic=True):
|
|
396
445
|
"""将数据集插入至mc已存在的表中.
|
|
397
446
|
|
|
398
447
|
Parameters
|
|
@@ -411,6 +460,53 @@ class ODPSRunner(object):
|
|
|
411
460
|
df.loc[:, "py_inserttime"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
412
461
|
|
|
413
462
|
if bool(partition):
|
|
463
|
+
if overwrite and atomic:
|
|
464
|
+
ts = datetime.now().strftime('%Y%m%d%H%M%S%f')
|
|
465
|
+
staging_partition = _make_related_odps_partition_spec(partition, f"__staging_{ts}")
|
|
466
|
+
backup_partition = _make_related_odps_partition_spec(partition, f"__old_{ts}")
|
|
467
|
+
target_exists = self._partition_exists(t, partition)
|
|
468
|
+
|
|
469
|
+
for stale in (staging_partition, backup_partition):
|
|
470
|
+
try:
|
|
471
|
+
t.delete_partition(stale, if_exists=True)
|
|
472
|
+
except Exception as cleanup_exc: # noqa: BLE001
|
|
473
|
+
logger.warning(
|
|
474
|
+
f"insert_df: failed to cleanup partition {stale} before atomic write: {cleanup_exc!r}"
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
with t.open_writer(partition=staging_partition, create_partition=True) as writer:
|
|
479
|
+
writer.write(df.values.tolist())
|
|
480
|
+
if target_exists:
|
|
481
|
+
self._rename_partition(table_name, partition, backup_partition)
|
|
482
|
+
self._rename_partition(table_name, staging_partition, partition)
|
|
483
|
+
except Exception:
|
|
484
|
+
if target_exists:
|
|
485
|
+
try:
|
|
486
|
+
if not self._partition_exists(t, partition) and self._partition_exists(t, backup_partition):
|
|
487
|
+
self._rename_partition(table_name, backup_partition, partition)
|
|
488
|
+
except Exception as restore_exc: # noqa: BLE001
|
|
489
|
+
logger.error(
|
|
490
|
+
f"insert_df: failed to restore original partition {partition} "
|
|
491
|
+
f"from {backup_partition}: {restore_exc!r}. Manual recovery may be required."
|
|
492
|
+
)
|
|
493
|
+
try:
|
|
494
|
+
t.delete_partition(staging_partition, if_exists=True)
|
|
495
|
+
except Exception: # noqa: BLE001
|
|
496
|
+
pass
|
|
497
|
+
raise
|
|
498
|
+
|
|
499
|
+
if target_exists:
|
|
500
|
+
try:
|
|
501
|
+
t.delete_partition(backup_partition, if_exists=True)
|
|
502
|
+
except Exception as cleanup_exc: # noqa: BLE001
|
|
503
|
+
logger.warning(
|
|
504
|
+
f"insert_df: atomic swap succeeded but failed to drop backup partition "
|
|
505
|
+
f"{backup_partition}: {cleanup_exc!r}. Safe to delete manually."
|
|
506
|
+
)
|
|
507
|
+
logger.info('<<<< insert_df atomic partition write done: shape={0} >>>>'.format(df.shape))
|
|
508
|
+
return
|
|
509
|
+
|
|
414
510
|
if overwrite:
|
|
415
511
|
t.delete_partition(partition, if_exists=True)
|
|
416
512
|
with t.open_writer(partition=partition, create_partition=True) as writer:
|
{supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Core/sample_weight_utils.py
RENAMED
|
@@ -52,6 +52,12 @@ def validate_sample_weight(weights, expected_len=None):
|
|
|
52
52
|
raise ValueError("sample_weight must be finite (no NaN/inf).")
|
|
53
53
|
if np.any(w < 0):
|
|
54
54
|
raise ValueError("sample_weight must be non-negative.")
|
|
55
|
+
total = float(np.sum(w))
|
|
56
|
+
if total <= 0:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
"sample_weight sum must be > 0; got sum={0}. "
|
|
59
|
+
"All-zero weights make weighted metrics undefined.".format(total)
|
|
60
|
+
)
|
|
55
61
|
return w
|
|
56
62
|
|
|
57
63
|
|
{supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/Model_Eval_Tool.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
import warnings
|
|
3
4
|
import numpy as np
|
|
4
5
|
import pandas as pd
|
|
5
6
|
from Modeling_Tool.Core.Binning_Tool import get_bin_range_list, super_binning
|
|
@@ -2246,8 +2247,9 @@ class PerformanceEvaluator:
|
|
|
2246
2247
|
self.weight_col = weight_col
|
|
2247
2248
|
self.datasets = {}
|
|
2248
2249
|
self.dataset_weight_cols = {}
|
|
2250
|
+
self.evaluate_status = None
|
|
2249
2251
|
|
|
2250
|
-
def add_dataset(self, name, data, weight_col = None):
|
|
2252
|
+
def add_dataset(self, name, data, weight_col = None, overwrite = False):
|
|
2251
2253
|
"""
|
|
2252
2254
|
添加数据集。
|
|
2253
2255
|
|
|
@@ -2263,6 +2265,17 @@ class PerformanceEvaluator:
|
|
|
2263
2265
|
self
|
|
2264
2266
|
返回自身以便链式调用
|
|
2265
2267
|
"""
|
|
2268
|
+
if name in self.datasets and not overwrite:
|
|
2269
|
+
raise KeyError(
|
|
2270
|
+
f"Dataset name {name!r} already exists. Use overwrite=True to replace it, "
|
|
2271
|
+
"or pass a different dataset name."
|
|
2272
|
+
)
|
|
2273
|
+
if name in self.datasets and overwrite:
|
|
2274
|
+
warnings.warn(
|
|
2275
|
+
f"PerformanceEvaluator.add_dataset overwriting existing dataset {name!r}.",
|
|
2276
|
+
RuntimeWarning,
|
|
2277
|
+
stacklevel=2,
|
|
2278
|
+
)
|
|
2266
2279
|
self.datasets[name] = data
|
|
2267
2280
|
self.dataset_weight_cols[name] = weight_col
|
|
2268
2281
|
return self
|
|
@@ -2304,16 +2317,38 @@ class PerformanceEvaluator:
|
|
|
2304
2317
|
``fig_save_path`` 自动按标签加后缀 (如 ``perf.png`` → ``perf_<label>.png``)。
|
|
2305
2318
|
"""
|
|
2306
2319
|
if len(self.datasets) == 0:
|
|
2320
|
+
self.evaluate_status = "no_datasets"
|
|
2307
2321
|
return pd.DataFrame([])
|
|
2308
2322
|
|
|
2309
2323
|
if self.scr_name is None and self.model is None and self.feature_cols is None:
|
|
2310
|
-
|
|
2324
|
+
self.evaluate_status = "compute_failed"
|
|
2325
|
+
warnings.warn(
|
|
2326
|
+
"PerformanceEvaluator.evaluate() now returns an empty DataFrame instead of -1; "
|
|
2327
|
+
"inspect .evaluate_status for the failure reason.",
|
|
2328
|
+
DeprecationWarning,
|
|
2329
|
+
stacklevel=2,
|
|
2330
|
+
)
|
|
2331
|
+
return pd.DataFrame([])
|
|
2311
2332
|
|
|
2312
2333
|
if self.scr_name is None and self.model is None:
|
|
2313
|
-
|
|
2334
|
+
self.evaluate_status = "compute_failed"
|
|
2335
|
+
warnings.warn(
|
|
2336
|
+
"PerformanceEvaluator.evaluate() now returns an empty DataFrame instead of -2; "
|
|
2337
|
+
"inspect .evaluate_status for the failure reason.",
|
|
2338
|
+
DeprecationWarning,
|
|
2339
|
+
stacklevel=2,
|
|
2340
|
+
)
|
|
2341
|
+
return pd.DataFrame([])
|
|
2314
2342
|
|
|
2315
2343
|
if self.scr_name is None and self.feature_cols is None:
|
|
2316
|
-
|
|
2344
|
+
self.evaluate_status = "compute_failed"
|
|
2345
|
+
warnings.warn(
|
|
2346
|
+
"PerformanceEvaluator.evaluate() now returns an empty DataFrame instead of -3; "
|
|
2347
|
+
"inspect .evaluate_status for the failure reason.",
|
|
2348
|
+
DeprecationWarning,
|
|
2349
|
+
stacklevel=2,
|
|
2350
|
+
)
|
|
2351
|
+
return pd.DataFrame([])
|
|
2317
2352
|
|
|
2318
2353
|
active_weight_col = weight_col or self.weight_col
|
|
2319
2354
|
has_dataset_weight = any(v is not None for v in self.dataset_weight_cols.values())
|
|
@@ -2369,6 +2404,7 @@ class PerformanceEvaluator:
|
|
|
2369
2404
|
)
|
|
2370
2405
|
if rpt_save_path:
|
|
2371
2406
|
fnl_df.to_csv(rpt_save_path, index=False)
|
|
2407
|
+
self.evaluate_status = "ok" if not fnl_df.empty else "empty_input"
|
|
2372
2408
|
return fnl_df
|
|
2373
2409
|
|
|
2374
2410
|
# ── 多 y 标签支持: tgt_name 为 list/tuple 时, 逐标签评估后纵向拼接 ──
|
|
@@ -2412,6 +2448,7 @@ class PerformanceEvaluator:
|
|
|
2412
2448
|
_ipy_display(fnl_df)
|
|
2413
2449
|
if rpt_save_path:
|
|
2414
2450
|
fnl_df.to_csv(rpt_save_path, index = False)
|
|
2451
|
+
self.evaluate_status = "ok" if not fnl_df.empty else "empty_input"
|
|
2415
2452
|
return fnl_df
|
|
2416
2453
|
|
|
2417
2454
|
def _get_score(data):
|
|
@@ -2472,6 +2509,7 @@ class PerformanceEvaluator:
|
|
|
2472
2509
|
}
|
|
2473
2510
|
|
|
2474
2511
|
if len(eval_datasets) == 0:
|
|
2512
|
+
self.evaluate_status = "empty_input"
|
|
2475
2513
|
return pd.DataFrame([])
|
|
2476
2514
|
|
|
2477
2515
|
model_eval_result_df = evaluate_performance(
|
|
@@ -2570,4 +2608,5 @@ class PerformanceEvaluator:
|
|
|
2570
2608
|
if rpt_save_path:
|
|
2571
2609
|
fnl_df.to_csv(rpt_save_path, index = False)
|
|
2572
2610
|
|
|
2611
|
+
self.evaluate_status = "ok" if isinstance(fnl_df, pd.DataFrame) and not fnl_df.empty else "empty_input"
|
|
2573
2612
|
return fnl_df
|
{supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/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): ...
|
|
36
|
-
def add_dataset(self, name, data): ...
|
|
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): ...
|
|
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): ...
|
|
36
|
+
def add_dataset(self, name, data, weight_col = None, overwrite = False): ...
|
|
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.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Eval/weighted_eval_utils.py
RENAMED
|
@@ -14,6 +14,7 @@ import pandas as pd
|
|
|
14
14
|
from sklearn.metrics import precision_recall_curve, roc_auc_score, roc_curve
|
|
15
15
|
|
|
16
16
|
from Modeling_Tool.Core.sample_weight_utils import resolve_sample_weight
|
|
17
|
+
from Modeling_Tool._utils.robust import smf_logger
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def resolve_weights(data=None, weight_col=None, sample_weight=None, expected_len=None, wgt=None, wgt_col=None):
|
|
@@ -43,7 +44,8 @@ def safe_weighted_average(values, weights=None):
|
|
|
43
44
|
def safe_auc(y_true, y_score, sample_weight=None):
|
|
44
45
|
try:
|
|
45
46
|
return float(roc_auc_score(y_true, y_score, sample_weight=sample_weight))
|
|
46
|
-
except
|
|
47
|
+
except (ValueError, ZeroDivisionError) as exc:
|
|
48
|
+
smf_logger.record_and_continue("auc", exc, stage="weighted_eval.safe_auc")
|
|
47
49
|
return np.nan
|
|
48
50
|
|
|
49
51
|
|
|
@@ -6,6 +6,7 @@ import pandas as pd
|
|
|
6
6
|
from sklearn.linear_model import LogisticRegression
|
|
7
7
|
import logging
|
|
8
8
|
from Modeling_Tool.Core.sample_weight_utils import resolve_sample_weight
|
|
9
|
+
from Modeling_Tool._utils.nan_guard import warn_if_nan_ratio_exceeds
|
|
9
10
|
|
|
10
11
|
logger = logging.getLogger(__name__)
|
|
11
12
|
|
|
@@ -252,6 +253,33 @@ def compute_bic(model, x, y, sample_weight=None):
|
|
|
252
253
|
return bic
|
|
253
254
|
|
|
254
255
|
|
|
256
|
+
def _prepare_nan_handled_frame(data, nan_handling="fillna_median", warn_threshold=0.05, context=""):
|
|
257
|
+
"""Return a numeric frame with explicit NaN handling for selection stats."""
|
|
258
|
+
allowed = {"fillna_0", "fillna_mean", "fillna_median", "drop_rows", "raise"}
|
|
259
|
+
if nan_handling not in allowed:
|
|
260
|
+
raise ValueError(f"nan_handling must be one of {sorted(allowed)}; got {nan_handling!r}")
|
|
261
|
+
|
|
262
|
+
frame = data.copy()
|
|
263
|
+
for col in frame.columns:
|
|
264
|
+
warn_if_nan_ratio_exceeds(frame[col], threshold=warn_threshold, context=f"{context}.{col}")
|
|
265
|
+
|
|
266
|
+
if nan_handling == "raise":
|
|
267
|
+
if frame.isna().any().any():
|
|
268
|
+
missing_cols = frame.columns[frame.isna().any()].tolist()
|
|
269
|
+
raise ValueError(f"{context}: NaN values present in columns {missing_cols}")
|
|
270
|
+
return frame
|
|
271
|
+
if nan_handling == "drop_rows":
|
|
272
|
+
return frame.dropna(axis=0)
|
|
273
|
+
if nan_handling == "fillna_0":
|
|
274
|
+
return frame.fillna(0)
|
|
275
|
+
|
|
276
|
+
if nan_handling == "fillna_mean":
|
|
277
|
+
fill_values = frame.mean(numeric_only=True).fillna(0)
|
|
278
|
+
else:
|
|
279
|
+
fill_values = frame.median(numeric_only=True).fillna(0)
|
|
280
|
+
return frame.fillna(fill_values).fillna(0)
|
|
281
|
+
|
|
282
|
+
|
|
255
283
|
class FeatureSelectionAnalyzer:
|
|
256
284
|
"""
|
|
257
285
|
Feature selection analyzer using statistical tests.
|
|
@@ -284,7 +312,7 @@ class FeatureSelectionAnalyzer:
|
|
|
284
312
|
self.selected_features_ = None
|
|
285
313
|
self.chi2_results_ = None
|
|
286
314
|
|
|
287
|
-
def chi2_selection(self, data, feature_cols, target_col):
|
|
315
|
+
def chi2_selection(self, data, feature_cols, target_col, nan_handling="fillna_median", nan_warn_threshold=0.05):
|
|
288
316
|
"""
|
|
289
317
|
Select features using chi-squared test.
|
|
290
318
|
|
|
@@ -305,8 +333,13 @@ class FeatureSelectionAnalyzer:
|
|
|
305
333
|
from sklearn.feature_selection import chi2
|
|
306
334
|
from sklearn.preprocessing import MinMaxScaler
|
|
307
335
|
|
|
308
|
-
x =
|
|
309
|
-
|
|
336
|
+
x = _prepare_nan_handled_frame(
|
|
337
|
+
data[feature_cols],
|
|
338
|
+
nan_handling=nan_handling,
|
|
339
|
+
warn_threshold=nan_warn_threshold,
|
|
340
|
+
context="FeatureSelectionAnalyzer.chi2_selection",
|
|
341
|
+
)
|
|
342
|
+
y = data.loc[x.index, target_col]
|
|
310
343
|
|
|
311
344
|
scaler = MinMaxScaler()
|
|
312
345
|
x_scaled = scaler.fit_transform(x)
|
|
@@ -324,7 +357,7 @@ class FeatureSelectionAnalyzer:
|
|
|
324
357
|
self.selected_features_ = results.loc[results['selected'], 'feature'].tolist()
|
|
325
358
|
return results
|
|
326
359
|
|
|
327
|
-
def compute_vif(self, data):
|
|
360
|
+
def compute_vif(self, data, nan_handling="fillna_median", nan_warn_threshold=0.05):
|
|
328
361
|
"""
|
|
329
362
|
Compute Variance Inflation Factor (VIF) for multicollinearity detection.
|
|
330
363
|
|
|
@@ -340,9 +373,15 @@ class FeatureSelectionAnalyzer:
|
|
|
340
373
|
"""
|
|
341
374
|
from statsmodels.stats.outliers_influence import variance_inflation_factor
|
|
342
375
|
|
|
343
|
-
|
|
376
|
+
work = _prepare_nan_handled_frame(
|
|
377
|
+
data,
|
|
378
|
+
nan_handling=nan_handling,
|
|
379
|
+
warn_threshold=nan_warn_threshold,
|
|
380
|
+
context="FeatureSelectionAnalyzer.compute_vif",
|
|
381
|
+
)
|
|
382
|
+
x = work.values
|
|
344
383
|
vif_data = pd.DataFrame({
|
|
345
|
-
'feature':
|
|
384
|
+
'feature': work.columns,
|
|
346
385
|
'VIF': [variance_inflation_factor(x, i) for i in range(x.shape[1])]
|
|
347
386
|
}).sort_values('VIF', ascending=False).reset_index(drop=True)
|
|
348
387
|
|
|
@@ -26,8 +26,8 @@ def compute_bic(model, x, y): ...
|
|
|
26
26
|
|
|
27
27
|
class FeatureSelectionAnalyzer:
|
|
28
28
|
def __init__(self, significance_level = 0.05): ...
|
|
29
|
-
def chi2_selection(self, data, feature_cols, target_col): ...
|
|
30
|
-
def compute_vif(self, data): ...
|
|
29
|
+
def chi2_selection(self, data, feature_cols, target_col, nan_handling = "fillna_median", nan_warn_threshold = 0.05): ...
|
|
30
|
+
def compute_vif(self, data, nan_handling = "fillna_median", nan_warn_threshold = 0.05): ...
|
|
31
31
|
def correlation_filter(self, data, threshold = 0.8): ...
|
|
32
32
|
|
|
33
33
|
class LRMaster:
|
{supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/__init__.py
RENAMED
|
@@ -31,6 +31,23 @@ from .feature_validation import (
|
|
|
31
31
|
)
|
|
32
32
|
from .orchestrator import run_modeling_from_validation
|
|
33
33
|
from .screening_artifact import FeatureScreeningArtifact, screen_result_to_summary
|
|
34
|
+
from .field_meta import (
|
|
35
|
+
FieldMeta,
|
|
36
|
+
PipelineRegistryEntry,
|
|
37
|
+
PIPELINE_REGISTRY,
|
|
38
|
+
config_from_dict,
|
|
39
|
+
config_from_yaml,
|
|
40
|
+
config_to_dict,
|
|
41
|
+
config_to_yaml,
|
|
42
|
+
extract_config_schema,
|
|
43
|
+
extract_pipeline_schema,
|
|
44
|
+
extract_schema,
|
|
45
|
+
generate_pipeline_code,
|
|
46
|
+
get_config_field_meta,
|
|
47
|
+
get_pipeline_registry,
|
|
48
|
+
get_pipeline_registry_schema,
|
|
49
|
+
validate_pipeline_config,
|
|
50
|
+
)
|
|
34
51
|
|
|
35
52
|
__all__ = [
|
|
36
53
|
"RejectInferencePipeline",
|
|
@@ -57,4 +74,19 @@ __all__ = [
|
|
|
57
74
|
"FeatureScreeningArtifact",
|
|
58
75
|
"screen_result_to_summary",
|
|
59
76
|
"run_modeling_from_validation",
|
|
77
|
+
"FieldMeta",
|
|
78
|
+
"PipelineRegistryEntry",
|
|
79
|
+
"PIPELINE_REGISTRY",
|
|
80
|
+
"get_pipeline_registry",
|
|
81
|
+
"get_pipeline_registry_schema",
|
|
82
|
+
"get_config_field_meta",
|
|
83
|
+
"extract_config_schema",
|
|
84
|
+
"extract_pipeline_schema",
|
|
85
|
+
"extract_schema",
|
|
86
|
+
"config_to_dict",
|
|
87
|
+
"config_from_dict",
|
|
88
|
+
"config_to_yaml",
|
|
89
|
+
"config_from_yaml",
|
|
90
|
+
"validate_pipeline_config",
|
|
91
|
+
"generate_pipeline_code",
|
|
60
92
|
]
|
|
@@ -285,7 +285,12 @@ def get_raw_model(model: Any) -> Any:
|
|
|
285
285
|
return model
|
|
286
286
|
|
|
287
287
|
|
|
288
|
-
def predict_positive(
|
|
288
|
+
def predict_positive(
|
|
289
|
+
model: Any,
|
|
290
|
+
data: pd.DataFrame,
|
|
291
|
+
feature_cols: list[str],
|
|
292
|
+
warn_nan: bool = True,
|
|
293
|
+
) -> np.ndarray:
|
|
289
294
|
raw_model = get_raw_model(model)
|
|
290
295
|
x = data[feature_cols]
|
|
291
296
|
|
|
@@ -338,7 +343,7 @@ def predict_positive(model: Any, data: pd.DataFrame, feature_cols: list[str]) ->
|
|
|
338
343
|
)
|
|
339
344
|
|
|
340
345
|
finite_mask = np.isfinite(result)
|
|
341
|
-
if not finite_mask.all():
|
|
346
|
+
if warn_nan and not finite_mask.all():
|
|
342
347
|
n_bad = int((~finite_mask).sum())
|
|
343
348
|
warnings.warn(
|
|
344
349
|
f"predict_positive: {n_bad}/{n_rows} predictions are NaN/Inf "
|
|
@@ -349,6 +354,35 @@ def predict_positive(model: Any, data: pd.DataFrame, feature_cols: list[str]) ->
|
|
|
349
354
|
return result
|
|
350
355
|
|
|
351
356
|
|
|
357
|
+
def predict_positive_many(
|
|
358
|
+
model: Any,
|
|
359
|
+
datasets: Mapping[str, pd.DataFrame],
|
|
360
|
+
feature_cols: list[str],
|
|
361
|
+
) -> tuple[dict[str, np.ndarray], dict[str, int]]:
|
|
362
|
+
"""Predict multiple datasets and emit one aggregate NaN/Inf warning."""
|
|
363
|
+
preds: dict[str, np.ndarray] = {}
|
|
364
|
+
stats: dict[str, int] = {}
|
|
365
|
+
total_bad = 0
|
|
366
|
+
total_rows = 0
|
|
367
|
+
for name, data in datasets.items():
|
|
368
|
+
pred = predict_positive(model, data, feature_cols, warn_nan=False)
|
|
369
|
+
bad = int((~np.isfinite(pred)).sum())
|
|
370
|
+
stats[str(name)] = bad
|
|
371
|
+
total_bad += bad
|
|
372
|
+
total_rows += len(pred)
|
|
373
|
+
preds[str(name)] = pred
|
|
374
|
+
|
|
375
|
+
if total_bad:
|
|
376
|
+
detail = ", ".join(f"{name}={count}" for name, count in stats.items() if count)
|
|
377
|
+
warnings.warn(
|
|
378
|
+
f"NaN/Inf predictions coerced or returned: {detail}; "
|
|
379
|
+
f"total {total_bad}/{total_rows}.",
|
|
380
|
+
RuntimeWarning,
|
|
381
|
+
stacklevel=2,
|
|
382
|
+
)
|
|
383
|
+
return preds, stats
|
|
384
|
+
|
|
385
|
+
|
|
352
386
|
def add_dataset_with_optional_weight(
|
|
353
387
|
evaluator: Any,
|
|
354
388
|
name: str,
|
{supermodelingfactory-0.5.2 → supermodelingfactory-0.5.4}/Modeling_Tool/Pipeline/credit_model.py
RENAMED
|
@@ -6,6 +6,7 @@ from typing import Any, Literal
|
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import pandas as pd
|
|
9
|
+
import warnings
|
|
9
10
|
|
|
10
11
|
from ._common import (
|
|
11
12
|
add_dataset_with_optional_weight,
|
|
@@ -175,6 +176,7 @@ class CreditModelPipeline:
|
|
|
175
176
|
|
|
176
177
|
def __init__(self, config: CreditModelPipelineConfig | None = None):
|
|
177
178
|
self.config = config or CreditModelPipelineConfig()
|
|
179
|
+
self.predict_positive_nan_stats: dict[str, dict[str, int]] = {}
|
|
178
180
|
self._validate_gbm_feature_source_config()
|
|
179
181
|
|
|
180
182
|
def run(self, data: pd.DataFrame) -> CreditModelPipelineResult:
|
|
@@ -1101,10 +1103,23 @@ class CreditModelPipeline:
|
|
|
1101
1103
|
equal_freq=True,
|
|
1102
1104
|
)
|
|
1103
1105
|
eval_splits = {**splits, **model_extra}
|
|
1106
|
+
nan_stats: dict[str, int] = {}
|
|
1104
1107
|
for ds_name, df in eval_splits.items():
|
|
1105
1108
|
scored = df.copy()
|
|
1106
1109
|
scored[f"pred_{name}"] = self._predict_model_positive(name, wrapper, scored, feature_cols)
|
|
1110
|
+
nan_stats[str(ds_name)] = int((~np.isfinite(scored[f"pred_{name}"].to_numpy(dtype=float))).sum())
|
|
1107
1111
|
add_dataset_with_optional_weight(evaluator, ds_name, scored, weight_col=cfg.weight_col)
|
|
1112
|
+
self.predict_positive_nan_stats[str(name)] = nan_stats
|
|
1113
|
+
total_bad = sum(nan_stats.values())
|
|
1114
|
+
if total_bad:
|
|
1115
|
+
total_rows = sum(len(df) for df in eval_splits.values())
|
|
1116
|
+
detail = ", ".join(f"{k}={v}" for k, v in nan_stats.items() if v)
|
|
1117
|
+
warnings.warn(
|
|
1118
|
+
f"{name}: NaN/Inf predictions detected across evaluation datasets: "
|
|
1119
|
+
f"{detail}; total {total_bad}/{total_rows}.",
|
|
1120
|
+
RuntimeWarning,
|
|
1121
|
+
stacklevel=2,
|
|
1122
|
+
)
|
|
1108
1123
|
fig_save_path = None
|
|
1109
1124
|
if cfg.write_outputs and cfg.plot_outputs:
|
|
1110
1125
|
fig_save_path = str(Path(cfg.output_dir) / "figs" / "perf" / f"perf_{name}.png")
|
|
@@ -1121,7 +1136,7 @@ class CreditModelPipeline:
|
|
|
1121
1136
|
if self._warm_start_requested_for(model_name) and model_name in {"lgb", "xgb"}:
|
|
1122
1137
|
init_score = self._get_warm_start_init_score(model_name, data)
|
|
1123
1138
|
return wrapper.predict_with_base_margin(data[feature_cols], init_score, return_prob=True)
|
|
1124
|
-
return predict_positive(wrapper, data, feature_cols)
|
|
1139
|
+
return predict_positive(wrapper, data, feature_cols, warn_nan=False)
|
|
1125
1140
|
|
|
1126
1141
|
def _will_run_explainability(self) -> bool:
|
|
1127
1142
|
cfg = self.config
|