SuperModelingFactory 0.3.11__tar.gz → 0.3.13__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.
Files changed (108) hide show
  1. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/Feature_Screen.py +137 -6
  2. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/Weighted_Screen.py +31 -0
  3. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/__init__.py +2 -0
  4. supermodelingfactory-0.3.13/Modeling_Tool/Pipeline/orchestrator.py +50 -0
  5. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/__init__.py +2 -2
  6. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/PKG-INFO +1 -1
  7. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/SuperModelingFactory.egg-info/PKG-INFO +1 -1
  8. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/SuperModelingFactory.egg-info/SOURCES.txt +1 -0
  9. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/pyproject.toml +1 -1
  10. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/setup.py +1 -1
  11. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/ExcelMaster/ExcelFormatTool.py +0 -0
  12. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/ExcelMaster/ExcelMaster.py +0 -0
  13. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/ExcelMaster/Template.py +0 -0
  14. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/ExcelMaster/Utility.py +0 -0
  15. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/ExcelMaster/__init__.py +0 -0
  16. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/LICENSE +0 -0
  17. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/MANIFEST.in +0 -0
  18. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Binning_Tool.py +0 -0
  19. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
  20. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
  21. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
  22. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
  23. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
  24. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
  25. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
  26. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Proc_Compare.py +0 -0
  27. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Slope_Tool.py +0 -0
  28. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
  29. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
  30. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
  31. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/__init__.py +0 -0
  32. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/kDataFrame.py +0 -0
  33. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
  34. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
  35. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Core/utils.py +0 -0
  36. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
  37. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
  38. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
  39. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
  40. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/__init__.py +0 -0
  41. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/evaluate_model.py +0 -0
  42. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
  43. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
  44. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
  45. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
  46. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Explainability/__init__.py +0 -0
  47. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
  48. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
  49. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
  50. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
  51. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
  52. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
  53. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
  54. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Feature/__init__.py +0 -0
  55. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/Backward_Tool.py +0 -0
  56. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
  57. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
  58. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/GBM_Tool.py +0 -0
  59. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
  60. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/LRM_Tool.py +0 -0
  61. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
  62. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Model/__init__.py +0 -0
  63. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/_common.py +0 -0
  64. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/credit_model.py +0 -0
  65. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/feature_validation.py +0 -0
  66. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
  67. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
  68. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
  69. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
  70. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
  71. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Pipeline/screening_artifact.py +0 -0
  72. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
  73. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
  74. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
  75. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
  76. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Sample/Sample_Split.py +0 -0
  77. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
  78. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/Sample/__init__.py +0 -0
  79. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
  80. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/UAT/__init__.py +0 -0
  81. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
  82. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
  83. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Master.py +0 -0
  84. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
  85. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
  86. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
  87. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
  88. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
  89. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
  90. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
  91. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
  92. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
  93. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/__init__.py +0 -0
  94. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
  95. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
  96. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
  97. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
  98. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/ref_font/__init__.py +0 -0
  99. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Modeling_Tool/ref_font/simsun.ttc +0 -0
  100. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/README.md +0 -0
  101. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Report/Report_Tool.py +0 -0
  102. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/Report/__init__.py +0 -0
  103. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
  104. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
  105. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/SuperModelingFactory.egg-info/requires.txt +0 -0
  106. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/SuperModelingFactory.egg-info/top_level.txt +0 -0
  107. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/requirements.txt +0 -0
  108. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.13}/setup.cfg +0 -0
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import warnings
6
5
  from dataclasses import dataclass, field
7
6
  from pathlib import Path
8
7
  from typing import Any, Mapping
@@ -10,11 +9,18 @@ from typing import Any, Mapping
10
9
  import numpy as np
11
10
  import pandas as pd
12
11
 
12
+ from Modeling_Tool.Core.sample_weight_utils import resolve_sample_weight
13
+
13
14
  from .Weighted_Screen import (
14
15
  WeightedScreenResult,
16
+ _corr_dedup_weighted,
15
17
  _legacy_unweighted_screen,
18
+ _psi_from_distributions,
16
19
  _resolve_splits,
17
20
  _summary_row,
21
+ _weighted_bin_distribution,
22
+ _weighted_iv_from_assigned_bins,
23
+ _weighted_pearson_corr_matrix,
18
24
  _weighted_screen_impl,
19
25
  )
20
26
 
@@ -289,6 +295,128 @@ def _woe_bins_unweighted_screen(
289
295
  )
290
296
 
291
297
 
298
+ def _bins_to_numpy(bins: Any) -> np.ndarray:
299
+ if isinstance(bins, pd.Series):
300
+ return bins.to_numpy(dtype=object)
301
+ return np.asarray(bins, dtype=object)
302
+
303
+
304
+ def _weighted_woe_bins_screen(
305
+ splits: dict[str, pd.DataFrame],
306
+ feature_cols: list[str],
307
+ target_col: str,
308
+ weight_col: str,
309
+ config: FeatureScreenConfig,
310
+ *,
311
+ prefit_woe_engine: Any | None = None,
312
+ ) -> FeatureScreenResult:
313
+ from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
314
+
315
+ ins, oos, oot = splits["ins"], splits["oos"], splits["oot"]
316
+ w_ins = resolve_sample_weight(data=ins, weight_col=weight_col, expected_len=len(ins))
317
+ y_ins = ins[target_col].astype(float).to_numpy()
318
+ current = list(feature_cols)
319
+ summary_rows = [_summary_row("initial", len(feature_cols), len(current), None, weight_col)]
320
+
321
+ binner = prefit_woe_engine
322
+ if binner is None:
323
+ binner = fit_screening_woe_engine(
324
+ ins,
325
+ feature_cols,
326
+ target_col,
327
+ woe_engine=config.woe_engine,
328
+ woe_fit_query=config.woe_fit_query,
329
+ woe_params=config.woe_params,
330
+ monotone_woe_params=config.monotone_woe_params,
331
+ categorical_features=config.categorical_features,
332
+ )
333
+ adapter = as_woe_engine(binner)
334
+
335
+ psi_records: list[dict] = []
336
+ if config.psi_enabled:
337
+ for var in feature_cols:
338
+ if var not in ins.columns or ins[var].nunique(dropna=False) <= 1:
339
+ continue
340
+ bins_ins = _bins_to_numpy(adapter.assign_bins(ins, var))
341
+ exp_dist = _weighted_bin_distribution(bins_ins, w_ins, config.content)
342
+ row: dict[str, Any] = {"var": var, "psi_ins_oos": np.nan, "psi_ins_oot": np.nan}
343
+ if "oos" in config.psi_compare_splits and len(oos) > 0:
344
+ w_oos = resolve_sample_weight(data=oos, weight_col=weight_col, expected_len=len(oos))
345
+ bins_oos = _bins_to_numpy(adapter.assign_bins(oos, var))
346
+ act = _weighted_bin_distribution(bins_oos, w_oos, config.content)
347
+ row["psi_ins_oos"] = _psi_from_distributions(exp_dist, act, config.content)
348
+ if "oot" in config.psi_compare_splits and len(oot) > 0:
349
+ w_oot = resolve_sample_weight(data=oot, weight_col=weight_col, expected_len=len(oot))
350
+ bins_oot = _bins_to_numpy(adapter.assign_bins(oot, var))
351
+ act = _weighted_bin_distribution(bins_oot, w_oot, config.content)
352
+ row["psi_ins_oot"] = _psi_from_distributions(exp_dist, act, config.content)
353
+ psi_records.append(row)
354
+
355
+ psi_table = pd.DataFrame(psi_records) if psi_records else pd.DataFrame(
356
+ columns=["var", "psi_ins_oos", "psi_ins_oot"],
357
+ )
358
+ if not psi_table.empty:
359
+ compare_cols = [c for c in ("psi_ins_oos", "psi_ins_oot") if c in psi_table.columns]
360
+ psi_table["psi_max"] = psi_table[compare_cols].max(axis=1, skipna=True)
361
+ keep = psi_table.loc[psi_table["psi_max"] < config.psi_threshold, "var"].tolist()
362
+ n_before = len(current)
363
+ current = [v for v in current if v in keep] or current
364
+ summary_rows.append(_summary_row("psi", n_before, len(current), config.psi_threshold, weight_col))
365
+ else:
366
+ psi_table["psi_max"] = pd.Series(dtype=float)
367
+ else:
368
+ psi_table = pd.DataFrame(columns=["var", "psi_ins_oos", "psi_ins_oot", "psi_max"])
369
+
370
+ iv_records: list[dict] = []
371
+ if config.iv_enabled:
372
+ for var in current:
373
+ if var not in ins.columns or ins[var].nunique(dropna=False) <= 1:
374
+ continue
375
+ bins_ins = _bins_to_numpy(adapter.assign_bins(ins, var))
376
+ x_ins = ins[var].to_numpy(dtype=float)
377
+ iv_val, n_b, miss = _weighted_iv_from_assigned_bins(y_ins, w_ins, bins_ins, x_ins)
378
+ iv_records.append({
379
+ "var": var,
380
+ "iv_weighted": iv_val,
381
+ "n_bins": n_b,
382
+ "missing_rate": miss,
383
+ })
384
+ iv_table = pd.DataFrame(iv_records) if iv_records else pd.DataFrame(
385
+ columns=["var", "iv_weighted", "n_bins", "missing_rate"],
386
+ )
387
+ if not iv_table.empty:
388
+ keep = iv_table.loc[iv_table["iv_weighted"] >= config.iv_threshold, "var"].tolist()
389
+ n_before = len(current)
390
+ current = [v for v in current if v in keep] or current
391
+ summary_rows.append(_summary_row("iv", n_before, len(current), config.iv_threshold, weight_col))
392
+ else:
393
+ iv_table = pd.DataFrame(columns=["var", "iv_weighted", "n_bins", "missing_rate"])
394
+
395
+ corr_dropped = pd.DataFrame(columns=["var_a", "var_b", "corr", "iv_a", "iv_b", "kept", "dropped"])
396
+ if config.corr_enabled and len(current) > 1:
397
+ X = ins[current].astype(float).to_numpy()
398
+ corr = _weighted_pearson_corr_matrix(X, w_ins)
399
+ iv_map = dict(zip(iv_table["var"], iv_table["iv_weighted"])) if not iv_table.empty else {}
400
+ n_before = len(current)
401
+ current, corr_dropped = _corr_dedup_weighted(
402
+ current,
403
+ corr,
404
+ iv_map,
405
+ config.corr_threshold,
406
+ config.corr_max_iterations,
407
+ )
408
+ summary_rows.append(_summary_row("corr", n_before, len(current), config.corr_threshold, weight_col))
409
+
410
+ summary_rows.append(_summary_row("final", len(feature_cols), len(current), None, weight_col))
411
+ return FeatureScreenResult(
412
+ selected_features=list(current),
413
+ iv_table=iv_table,
414
+ psi_table=psi_table,
415
+ corr_dropped=corr_dropped,
416
+ summary=pd.DataFrame(summary_rows),
417
+ )
418
+
419
+
292
420
  def feature_screen(
293
421
  splits: dict[str, pd.DataFrame],
294
422
  feature_cols: list[str],
@@ -303,11 +431,14 @@ def feature_screen(
303
431
  use_woe_bins = _needs_woe_bins(cfg)
304
432
 
305
433
  if weight_col is not None:
306
- if use_woe_bins:
307
- warnings.warn(
308
- "Weighted feature screening with psi/iv/corr_use_woe_bins is not fully "
309
- "supported yet; falling back to weighted equal-frequency bins.",
310
- stacklevel=2,
434
+ if use_woe_bins or prefit_woe_engine is not None:
435
+ return _weighted_woe_bins_screen(
436
+ splits,
437
+ feature_cols,
438
+ target_col,
439
+ weight_col,
440
+ cfg,
441
+ prefit_woe_engine=prefit_woe_engine,
311
442
  )
312
443
  return _weighted_screen_impl(
313
444
  splits,
@@ -119,6 +119,37 @@ def _psi_from_distributions(expected: pd.Series, actual: pd.Series, content: flo
119
119
  return float(((a - e) * np.log(a / e)).sum())
120
120
 
121
121
 
122
+ def _weighted_iv_from_assigned_bins(
123
+ y: np.ndarray,
124
+ w: np.ndarray,
125
+ bins: np.ndarray,
126
+ x: np.ndarray,
127
+ ) -> tuple[float, int, float]:
128
+ total_bad = float(np.sum(w * y))
129
+ total_good = float(np.sum(w * (1.0 - y)))
130
+ if total_bad <= 0 or total_good <= 0:
131
+ missing_rate = 1.0 - float(np.sum(w[np.isfinite(x)])) / float(np.sum(w) or 1.0)
132
+ return 0.0, 0, missing_rate
133
+
134
+ rows = []
135
+ for b in pd.unique(bins):
136
+ m = bins == b
137
+ bad_w = float(np.sum(w[m] * y[m]))
138
+ good_w = float(np.sum(w[m] * (1.0 - y[m])))
139
+ rows.append({
140
+ "bad_pct": bad_w / total_bad,
141
+ "good_pct": good_w / total_good,
142
+ })
143
+ if not rows:
144
+ missing_rate = 1.0 - float(np.sum(w[np.isfinite(x)])) / float(np.sum(w) or 1.0)
145
+ return 0.0, 0, missing_rate
146
+
147
+ stats = pd.DataFrame(rows)
148
+ iv = float(calc_iv(stats, "bad_pct", "good_pct").sum())
149
+ missing_rate = 1.0 - float(np.sum(w[np.isfinite(x)])) / float(np.sum(w) or 1.0)
150
+ return iv, len(rows), missing_rate
151
+
152
+
122
153
  def _weighted_iv_for_var(
123
154
  x: np.ndarray,
124
155
  y: np.ndarray,
@@ -29,6 +29,7 @@ from .feature_validation import (
29
29
  FeatureValidationPipelineConfig,
30
30
  FeatureValidationPipelineResult,
31
31
  )
32
+ from .orchestrator import run_modeling_from_validation
32
33
  from .screening_artifact import FeatureScreeningArtifact, screen_result_to_summary
33
34
 
34
35
  __all__ = [
@@ -55,4 +56,5 @@ __all__ = [
55
56
  "FeatureValidationPipelineResult",
56
57
  "FeatureScreeningArtifact",
57
58
  "screen_result_to_summary",
59
+ "run_modeling_from_validation",
58
60
  ]
@@ -0,0 +1,50 @@
1
+ """High-level orchestration helpers across SMF pipelines."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import replace
6
+ from typing import Any
7
+
8
+ import pandas as pd
9
+
10
+ from .credit_model import CreditModelPipeline, CreditModelPipelineConfig, CreditModelPipelineResult
11
+ from .feature_validation import (
12
+ FeatureValidationPipeline,
13
+ FeatureValidationPipelineConfig,
14
+ FeatureValidationPipelineResult,
15
+ )
16
+ from .screening_artifact import FeatureScreeningArtifact
17
+
18
+
19
+ def run_modeling_from_validation(
20
+ data: pd.DataFrame,
21
+ *,
22
+ fvp_config: FeatureValidationPipelineConfig | None = None,
23
+ cm_config: CreditModelPipelineConfig | None = None,
24
+ selection_enabled: bool = True,
25
+ reuse_screening_woe: bool = True,
26
+ ) -> tuple[FeatureValidationPipelineResult, CreditModelPipelineResult]:
27
+ """Run feature validation/selection, then credit modeling on the same dataset."""
28
+ fvp_cfg = fvp_config or FeatureValidationPipelineConfig()
29
+ if selection_enabled and not fvp_cfg.selection_enabled:
30
+ fvp_cfg = replace(fvp_cfg, selection_enabled=True)
31
+
32
+ fvp_result = FeatureValidationPipeline(fvp_cfg).run(data)
33
+ artifact = FeatureScreeningArtifact.from_fvp_result(fvp_result)
34
+
35
+ cm_cfg = cm_config or CreditModelPipelineConfig()
36
+ cm_overrides: dict[str, Any] = {
37
+ "screening_artifact": artifact,
38
+ "reuse_screening_woe": reuse_screening_woe,
39
+ "target_col": artifact.target_col,
40
+ "weight_col": artifact.weight_col,
41
+ }
42
+ if artifact.selected_features:
43
+ cm_overrides["feature_cols"] = list(artifact.selected_features)
44
+ cm_cfg = replace(cm_cfg, **cm_overrides)
45
+
46
+ cm_result = CreditModelPipeline(cm_cfg).run(data)
47
+ return fvp_result, cm_result
48
+
49
+
50
+ __all__ = ["run_modeling_from_validation"]
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  __author__ = "Jingkai Sun"
4
- __version__ = "0.3.11"
4
+ __version__ = "0.3.13"
5
5
 
6
6
  # Core module - base data structures
7
7
  from .Core import (
@@ -116,7 +116,7 @@ _PIPELINE_EXPORTS = frozenset({
116
116
  'SampleAnalysisPipeline', 'SampleAnalysisPipelineConfig', 'SampleAnalysisPipelineResult',
117
117
  'MockSamplePipeline', 'MockSamplePipelineConfig', 'MockSamplePipelineResult',
118
118
  'FeatureValidationPipeline', 'FeatureValidationPipelineConfig', 'FeatureValidationPipelineResult',
119
- 'FeatureScreeningArtifact', 'screen_result_to_summary',
119
+ 'FeatureScreeningArtifact', 'screen_result_to_summary', 'run_modeling_from_validation',
120
120
  })
121
121
 
122
122
  def __getattr__(name):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SuperModelingFactory
3
- Version: 0.3.11
3
+ Version: 0.3.13
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SuperModelingFactory
3
- Version: 0.3.11
3
+ Version: 0.3.13
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
@@ -62,6 +62,7 @@ Modeling_Tool/Pipeline/_common.py
62
62
  Modeling_Tool/Pipeline/credit_model.py
63
63
  Modeling_Tool/Pipeline/feature_validation.py
64
64
  Modeling_Tool/Pipeline/mock_sample.py
65
+ Modeling_Tool/Pipeline/orchestrator.py
65
66
  Modeling_Tool/Pipeline/reject_inference.py
66
67
  Modeling_Tool/Pipeline/sample_analysis.py
67
68
  Modeling_Tool/Pipeline/score_comparison.py
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
12
12
 
13
13
  [project]
14
14
  name = "SuperModelingFactory"
15
- version = "0.3.11"
15
+ version = "0.3.13"
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.3.11"),
18
+ version=os.environ.get("SMF_VERSION", "0.3.13"),
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",