SuperModelingFactory 0.5.1__tar.gz → 0.5.3__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.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Binning_Tool.py +1 -2
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/ODPS_Tool.py +125 -6
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/sample_weight_utils.py +6 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/Model_Eval_Tool.py +43 -4
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +3 -3
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/weighted_eval_utils.py +3 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/LRM_Tool.py +45 -6
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/LRM_Tool.pyi +2 -2
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/_common.py +36 -2
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/credit_model.py +16 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/reject_inference.py +16 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -2
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Reject_Infer.py +50 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Reject_Infer.pyi +1 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/__init__.py +1 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/_utils/__init__.py +2 -1
- supermodelingfactory-0.5.3/Modeling_Tool/_utils/nan_guard.py +31 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/PKG-INFO +2 -2
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/README.md +1 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/PKG-INFO +2 -2
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/SOURCES.txt +1 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/pyproject.toml +1 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/setup.py +1 -1
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/ExcelMaster/ExcelFormatTool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/ExcelMaster/ExcelMaster.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/ExcelMaster/Template.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/ExcelMaster/Utility.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/ExcelMaster/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/LICENSE +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/MANIFEST.in +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Proc_Compare.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Slope_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/kDataFrame.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/utils.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/evaluate_model.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Explainability/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Feature_Screen.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Weighted_Screen.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/Backward_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/GBM_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/feature_validation.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/screening_artifact.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Sample_Split.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/UAT/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Master.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/_utils/robust.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/_utils/sentinels.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/ref_font/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/ref_font/simsun.ttc +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Report/Report_Tool.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Report/__init__.py +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/requires.txt +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/top_level.txt +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/requirements.txt +0 -0
- {supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/setup.cfg +0 -0
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/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)
|
|
@@ -17,6 +17,63 @@ pd.options.mode.chained_assignment = None # default='warn'
|
|
|
17
17
|
|
|
18
18
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
19
19
|
|
|
20
|
+
|
|
21
|
+
def _split_odps_table_name(table_name):
|
|
22
|
+
"""Return (qualifier, bare table name) for ODPS table identifiers.
|
|
23
|
+
|
|
24
|
+
MaxCompute accepts a project-qualified source in ``ALTER TABLE`` but the
|
|
25
|
+
``RENAME TO`` target must be the bare table name within the same project.
|
|
26
|
+
"""
|
|
27
|
+
table_name = str(table_name)
|
|
28
|
+
if "." not in table_name:
|
|
29
|
+
return "", table_name
|
|
30
|
+
qualifier, bare_name = table_name.rsplit(".", 1)
|
|
31
|
+
return qualifier, bare_name
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _make_related_odps_table_name(table_name, suffix):
|
|
35
|
+
qualifier, bare_name = _split_odps_table_name(table_name)
|
|
36
|
+
related_bare = f"{bare_name}{suffix}"
|
|
37
|
+
if qualifier:
|
|
38
|
+
return f"{qualifier}.{related_bare}", related_bare
|
|
39
|
+
return related_bare, related_bare
|
|
40
|
+
|
|
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
|
+
|
|
20
77
|
class ODPSRunner(object):
|
|
21
78
|
_wide_schema_patch_lock = threading.RLock()
|
|
22
79
|
_wide_schema_patch_ref_count = 0
|
|
@@ -292,8 +349,9 @@ class ODPSRunner(object):
|
|
|
292
349
|
# Atomic swap path (default from 0.4.2). If any step before the final
|
|
293
350
|
# swap fails, the original table is untouched.
|
|
294
351
|
ts = datetime.now().strftime('%Y%m%d%H%M%S%f')
|
|
295
|
-
|
|
296
|
-
|
|
352
|
+
_, target_rename_name = _split_odps_table_name(table_name)
|
|
353
|
+
tmp_name, _ = _make_related_odps_table_name(table_name, f"__tmp_{ts}")
|
|
354
|
+
old_name, old_rename_name = _make_related_odps_table_name(table_name, f"__old_{ts}")
|
|
297
355
|
|
|
298
356
|
# Step 1: write data into the temp table. If this fails, drop the
|
|
299
357
|
# partial temp table and re-raise; the target table is untouched.
|
|
@@ -321,7 +379,7 @@ class ODPSRunner(object):
|
|
|
321
379
|
target_exists = self.o.exist_table(table_name)
|
|
322
380
|
if target_exists:
|
|
323
381
|
try:
|
|
324
|
-
self.o.run_sql(f"ALTER TABLE {table_name} RENAME TO {
|
|
382
|
+
self.o.run_sql(f"ALTER TABLE {table_name} RENAME TO {old_rename_name};")
|
|
325
383
|
except Exception:
|
|
326
384
|
# Rename of the live target failed — do not touch it. Drop the
|
|
327
385
|
# tmp table so we don't leak it, then re-raise.
|
|
@@ -334,13 +392,13 @@ class ODPSRunner(object):
|
|
|
334
392
|
raise
|
|
335
393
|
|
|
336
394
|
try:
|
|
337
|
-
self.o.run_sql(f"ALTER TABLE {tmp_name} RENAME TO {
|
|
395
|
+
self.o.run_sql(f"ALTER TABLE {tmp_name} RENAME TO {target_rename_name};")
|
|
338
396
|
except Exception:
|
|
339
397
|
# tmp -> target rename failed. If we already moved the original,
|
|
340
398
|
# try to restore it so the caller is not left with a missing table.
|
|
341
399
|
if target_exists:
|
|
342
400
|
try:
|
|
343
|
-
self.o.run_sql(f"ALTER TABLE {old_name} RENAME TO {
|
|
401
|
+
self.o.run_sql(f"ALTER TABLE {old_name} RENAME TO {target_rename_name};")
|
|
344
402
|
logger.warning(
|
|
345
403
|
f"upload_df: swap of {tmp_name} -> {table_name} failed; restored original from {old_name}"
|
|
346
404
|
)
|
|
@@ -369,7 +427,21 @@ class ODPSRunner(object):
|
|
|
369
427
|
|
|
370
428
|
logger.info(f'<<<< 完成数据入表{table_name}: shape={df.shape} >>>>')
|
|
371
429
|
|
|
372
|
-
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):
|
|
373
445
|
"""将数据集插入至mc已存在的表中.
|
|
374
446
|
|
|
375
447
|
Parameters
|
|
@@ -388,6 +460,53 @@ class ODPSRunner(object):
|
|
|
388
460
|
df.loc[:, "py_inserttime"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
389
461
|
|
|
390
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
|
+
|
|
391
510
|
if overwrite:
|
|
392
511
|
t.delete_partition(partition, if_exists=True)
|
|
393
512
|
with t.open_writer(partition=partition, create_partition=True) as writer:
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/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.1 → supermodelingfactory-0.5.3}/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.1 → supermodelingfactory-0.5.3}/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.1 → supermodelingfactory-0.5.3}/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:
|
|
@@ -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.1 → supermodelingfactory-0.5.3}/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
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/reject_inference.py
RENAMED
|
@@ -128,6 +128,7 @@ class RejectInferencePipeline:
|
|
|
128
128
|
|
|
129
129
|
def __init__(self, config: RejectInferencePipelineConfig | None = None):
|
|
130
130
|
self.config = config or RejectInferencePipelineConfig()
|
|
131
|
+
self.predict_positive_nan_stats: dict[str, dict[str, int]] = {}
|
|
131
132
|
|
|
132
133
|
def run(self, data: pd.DataFrame) -> RejectInferencePipelineResult:
|
|
133
134
|
cfg = self.config
|
|
@@ -718,8 +719,22 @@ class RejectInferencePipeline:
|
|
|
718
719
|
|
|
719
720
|
train_eval = train[train[cfg.approved_col] == 1].copy()
|
|
720
721
|
eval_sets = {"train": train_eval, "validation": val.copy(), "oot": oot.copy()}
|
|
722
|
+
nan_stats: dict[str, int] = {}
|
|
721
723
|
for ds in eval_sets.values():
|
|
722
|
-
ds["pred_prob"] = predict_positive(model, ds, feature_cols)
|
|
724
|
+
ds["pred_prob"] = predict_positive(model, ds, feature_cols, warn_nan=False)
|
|
725
|
+
for ds_name, ds in eval_sets.items():
|
|
726
|
+
nan_stats[ds_name] = int((~np.isfinite(ds["pred_prob"].to_numpy(dtype=float))).sum())
|
|
727
|
+
self.predict_positive_nan_stats[str(method)] = nan_stats
|
|
728
|
+
total_bad = sum(nan_stats.values())
|
|
729
|
+
if total_bad:
|
|
730
|
+
total_rows = sum(len(ds) for ds in eval_sets.values())
|
|
731
|
+
detail = ", ".join(f"{k}={v}" for k, v in nan_stats.items() if v)
|
|
732
|
+
warnings.warn(
|
|
733
|
+
f"{method}: NaN/Inf predictions detected across RI evaluation datasets: "
|
|
734
|
+
f"{detail}; total {total_bad}/{total_rows}.",
|
|
735
|
+
RuntimeWarning,
|
|
736
|
+
stacklevel=2,
|
|
737
|
+
)
|
|
723
738
|
|
|
724
739
|
evaluator = PerformanceEvaluator(
|
|
725
740
|
tgt_name=cfg.target_col,
|
|
@@ -242,8 +242,6 @@ class ScoreConsistencyUATPipeline:
|
|
|
242
242
|
coerced_count = int((as_num.notna() & original_notna).sum())
|
|
243
243
|
parse_ratio = coerced_count / original_count
|
|
244
244
|
if mode == "aggressive":
|
|
245
|
-
if coerced_count == 0:
|
|
246
|
-
continue
|
|
247
245
|
lost = original_count - coerced_count
|
|
248
246
|
if lost > 0:
|
|
249
247
|
_logger.warning(
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Reject_Infer.py
RENAMED
|
@@ -28,6 +28,8 @@ import numpy as np
|
|
|
28
28
|
from typing import Union, Optional, List, Dict, Any, Tuple
|
|
29
29
|
from abc import ABC, abstractmethod
|
|
30
30
|
|
|
31
|
+
from Modeling_Tool._utils.nan_guard import warn_if_nan_ratio_exceeds
|
|
32
|
+
|
|
31
33
|
|
|
32
34
|
class RejectInferrer(ABC):
|
|
33
35
|
"""
|
|
@@ -81,6 +83,45 @@ class RejectInferrer(ABC):
|
|
|
81
83
|
|
|
82
84
|
def _rng(self) -> np.random.Generator:
|
|
83
85
|
return np.random.default_rng(self.random_state)
|
|
86
|
+
|
|
87
|
+
def _filter_nan_bad_probability(
|
|
88
|
+
self,
|
|
89
|
+
df_rejected: pd.DataFrame,
|
|
90
|
+
p_bad: pd.Series,
|
|
91
|
+
score_col: str,
|
|
92
|
+
nan_score_policy: str,
|
|
93
|
+
) -> Tuple[pd.DataFrame, pd.Series]:
|
|
94
|
+
if nan_score_policy not in {"drop", "raise", "fill_0.5"}:
|
|
95
|
+
raise ValueError("nan_score_policy must be one of 'drop', 'raise', or 'fill_0.5'")
|
|
96
|
+
|
|
97
|
+
stats = warn_if_nan_ratio_exceeds(
|
|
98
|
+
p_bad,
|
|
99
|
+
threshold=0.01,
|
|
100
|
+
context="reject inference bad probability",
|
|
101
|
+
)
|
|
102
|
+
n_nan = int(stats["n_nan"])
|
|
103
|
+
if n_nan == 0:
|
|
104
|
+
return df_rejected, p_bad
|
|
105
|
+
|
|
106
|
+
n_total = int(stats["n_total"])
|
|
107
|
+
if nan_score_policy == "raise":
|
|
108
|
+
raise ValueError(
|
|
109
|
+
f"Reject inference bad probability contains {n_nan}/{n_total} NaN values "
|
|
110
|
+
f"from score_col={score_col!r}."
|
|
111
|
+
)
|
|
112
|
+
if nan_score_policy == "fill_0.5":
|
|
113
|
+
return df_rejected, p_bad.fillna(0.5)
|
|
114
|
+
|
|
115
|
+
keep = p_bad.notna()
|
|
116
|
+
dropped = int((~keep).sum())
|
|
117
|
+
warnings.warn(
|
|
118
|
+
f"Reject inference dropped {dropped}/{n_total} rejected rows with NaN bad probability "
|
|
119
|
+
f"from score_col={score_col!r}. Set nan_score_policy='fill_0.5' to reproduce "
|
|
120
|
+
f"the legacy 50/50 behaviour.",
|
|
121
|
+
RuntimeWarning,
|
|
122
|
+
stacklevel=2,
|
|
123
|
+
)
|
|
124
|
+
return df_rejected.loc[keep].copy(), p_bad.loc[keep]
|
|
84
125
|
|
|
85
126
|
@abstractmethod
|
|
86
127
|
def infer(self, df_approved: pd.DataFrame,
|
|
@@ -315,12 +356,14 @@ class FuzzyAugmentInferrer(RejectInferrer):
|
|
|
315
356
|
weight_factor: float = 1.0,
|
|
316
357
|
score_direction: str = 'high_good',
|
|
317
358
|
random_state: Optional[int] = None,
|
|
359
|
+
nan_score_policy: str = "drop",
|
|
318
360
|
):
|
|
319
361
|
"""
|
|
320
362
|
Initialize FuzzyAugmentInferrer.
|
|
321
363
|
"""
|
|
322
364
|
super().__init__(target_col, score_col, score_direction=score_direction, random_state=random_state)
|
|
323
365
|
self.weight_factor = weight_factor
|
|
366
|
+
self.nan_score_policy = nan_score_policy
|
|
324
367
|
|
|
325
368
|
def infer(self, df_approved: pd.DataFrame,
|
|
326
369
|
df_rejected: pd.DataFrame,
|
|
@@ -347,7 +390,13 @@ class FuzzyAugmentInferrer(RejectInferrer):
|
|
|
347
390
|
df_approved_copy = df_approved.copy()
|
|
348
391
|
df_approved_copy['_weight'] = 1.0
|
|
349
392
|
|
|
350
|
-
p_bad = self._bad_probability(df_rejected[score_col])
|
|
393
|
+
p_bad = self._bad_probability(df_rejected[score_col])
|
|
394
|
+
df_rejected, p_bad = self._filter_nan_bad_probability(
|
|
395
|
+
df_rejected,
|
|
396
|
+
p_bad,
|
|
397
|
+
score_col=score_col,
|
|
398
|
+
nan_score_policy=self.nan_score_policy,
|
|
399
|
+
)
|
|
351
400
|
bad_copy = df_rejected.copy()
|
|
352
401
|
bad_copy[self.target_col] = 1
|
|
353
402
|
bad_copy['_weight'] = p_bad.to_numpy(dtype=float) * float(self.weight_factor)
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Reject_Infer.pyi
RENAMED
|
@@ -31,7 +31,7 @@ class HardCutoffInferrer(RejectInferrer):
|
|
|
31
31
|
def infer(self, df_approved: pd.DataFrame, df_rejected: pd.DataFrame, score_col: Optional[str] = None) -> pd.DataFrame: ...
|
|
32
32
|
|
|
33
33
|
class FuzzyAugmentInferrer(RejectInferrer):
|
|
34
|
-
def __init__(self, target_col: str = 'target', score_col: str = 'score', weight_factor: float = 1.0, score_direction: str = 'high_good', random_state: Optional[int] = None): ...
|
|
34
|
+
def __init__(self, target_col: str = 'target', score_col: str = 'score', weight_factor: float = 1.0, score_direction: str = 'high_good', random_state: Optional[int] = None, nan_score_policy: str = "drop"): ...
|
|
35
35
|
def infer(self, df_approved: pd.DataFrame, df_rejected: pd.DataFrame, score_col: Optional[str] = None) -> pd.DataFrame: ...
|
|
36
36
|
|
|
37
37
|
class ParcelingInferrer(RejectInferrer):
|
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from .robust import smf_logger, iv_guard
|
|
4
4
|
from .sentinels import SMF_MISSING_BIN
|
|
5
|
+
from .nan_guard import warn_if_nan_ratio_exceeds
|
|
5
6
|
|
|
6
|
-
__all__ = ["smf_logger", "iv_guard", "SMF_MISSING_BIN"]
|
|
7
|
+
__all__ = ["smf_logger", "iv_guard", "SMF_MISSING_BIN", "warn_if_nan_ratio_exceeds"]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
"""Shared NaN warning helpers."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def warn_if_nan_ratio_exceeds(values, threshold: float = 0.05, context: str = "") -> dict:
|
|
12
|
+
"""Warn when the NaN/Inf share in ``values`` exceeds ``threshold``.
|
|
13
|
+
|
|
14
|
+
Returns a small stats dict so callers can surface the same information in
|
|
15
|
+
reports without recomputing it.
|
|
16
|
+
"""
|
|
17
|
+
arr = pd.to_numeric(pd.Series(values), errors="coerce")
|
|
18
|
+
n_total = int(len(arr))
|
|
19
|
+
n_nan = int((~np.isfinite(arr.to_numpy(dtype=float))).sum()) if n_total else 0
|
|
20
|
+
ratio = n_nan / n_total if n_total else 0.0
|
|
21
|
+
if n_total and ratio > float(threshold):
|
|
22
|
+
label = f"{context}: " if context else ""
|
|
23
|
+
warnings.warn(
|
|
24
|
+
f"{label}{n_nan}/{n_total} ({ratio:.1%}) values are NaN/Inf.",
|
|
25
|
+
RuntimeWarning,
|
|
26
|
+
stacklevel=2,
|
|
27
|
+
)
|
|
28
|
+
return {"n_total": n_total, "n_nan": n_nan, "nan_ratio": ratio}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = ["warn_if_nan_ratio_exceeds"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SuperModelingFactory
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
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.
|
|
268
|
+
- **Version**: 0.5.3
|
|
269
269
|
- **Author**: Jingkai Sun
|
|
270
270
|
|
|
271
271
|
## 许可证
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SuperModelingFactory
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
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.
|
|
268
|
+
- **Version**: 0.5.3
|
|
269
269
|
- **Author**: Jingkai Sun
|
|
270
270
|
|
|
271
271
|
## 许可证
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/SOURCES.txt
RENAMED
|
@@ -93,6 +93,7 @@ Modeling_Tool/WOE/__init__.py
|
|
|
93
93
|
Modeling_Tool/WOE/plot_woe_tool.py
|
|
94
94
|
Modeling_Tool/WOE/plot_woe_tool.pyi
|
|
95
95
|
Modeling_Tool/_utils/__init__.py
|
|
96
|
+
Modeling_Tool/_utils/nan_guard.py
|
|
96
97
|
Modeling_Tool/_utils/robust.py
|
|
97
98
|
Modeling_Tool/_utils/sentinels.py
|
|
98
99
|
Modeling_Tool/ref_font/KaiTi.ttf
|
|
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
|
|
|
12
12
|
|
|
13
13
|
[project]
|
|
14
14
|
name = "SuperModelingFactory"
|
|
15
|
-
version = "0.5.
|
|
15
|
+
version = "0.5.3"
|
|
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.5.
|
|
18
|
+
version=os.environ.get("SMF_VERSION", "0.5.3"),
|
|
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.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Binning_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Json_Data_Converter.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Model_Registry_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Parallel_Engine.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/Proc_Compare.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/XOR_Encryptor.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Core/XOR_Encryptor.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/Evaluation_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/Evaluation_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/evaluate_model.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Eval/evaluate_model.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Explainability/__init__.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Distribution_Tool.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Feature_Insights.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Feature_Insights.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Feature_Screen.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/PSI_Tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Feature/Weighted_Screen.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/Backward_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/Backward_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Model/GBM_Search_Tool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/mock_sample.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/orchestrator.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/sample_analysis.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Pipeline/score_comparison.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/Sample/Sample_Split.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/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.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Monotone_Binner.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Plot_Tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Report_Builder.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/WOE_Report_Builder.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/plot_woe_tool.py
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/WOE/plot_woe_tool.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/Modeling_Tool/ref_font/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{supermodelingfactory-0.5.1 → supermodelingfactory-0.5.3}/SuperModelingFactory.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|