SuperModelingFactory 0.3.11__tar.gz → 0.3.12__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 (107) hide show
  1. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/Feature_Screen.py +137 -6
  2. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/Weighted_Screen.py +31 -0
  3. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/__init__.py +1 -1
  4. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/PKG-INFO +1 -1
  5. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/SuperModelingFactory.egg-info/PKG-INFO +1 -1
  6. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/pyproject.toml +1 -1
  7. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/setup.py +1 -1
  8. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/ExcelMaster/ExcelFormatTool.py +0 -0
  9. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/ExcelMaster/ExcelMaster.py +0 -0
  10. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/ExcelMaster/Template.py +0 -0
  11. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/ExcelMaster/Utility.py +0 -0
  12. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/ExcelMaster/__init__.py +0 -0
  13. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/LICENSE +0 -0
  14. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/MANIFEST.in +0 -0
  15. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Binning_Tool.py +0 -0
  16. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
  17. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
  18. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
  19. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
  20. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
  21. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
  22. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
  23. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Proc_Compare.py +0 -0
  24. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Slope_Tool.py +0 -0
  25. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
  26. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
  27. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
  28. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/__init__.py +0 -0
  29. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/kDataFrame.py +0 -0
  30. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
  31. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
  32. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Core/utils.py +0 -0
  33. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
  34. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
  35. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
  36. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
  37. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/__init__.py +0 -0
  38. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/evaluate_model.py +0 -0
  39. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
  40. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
  41. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
  42. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
  43. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Explainability/__init__.py +0 -0
  44. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
  45. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
  46. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
  47. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
  48. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
  49. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
  50. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
  51. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Feature/__init__.py +0 -0
  52. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/Backward_Tool.py +0 -0
  53. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
  54. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
  55. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/GBM_Tool.py +0 -0
  56. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
  57. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/LRM_Tool.py +0 -0
  58. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
  59. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Model/__init__.py +0 -0
  60. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/__init__.py +0 -0
  61. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/_common.py +0 -0
  62. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/credit_model.py +0 -0
  63. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/feature_validation.py +0 -0
  64. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
  65. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
  66. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
  67. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
  68. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
  69. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Pipeline/screening_artifact.py +0 -0
  70. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
  71. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
  72. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
  73. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
  74. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Sample/Sample_Split.py +0 -0
  75. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
  76. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/Sample/__init__.py +0 -0
  77. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
  78. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/UAT/__init__.py +0 -0
  79. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
  80. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
  81. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Master.py +0 -0
  82. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
  83. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
  84. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
  85. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
  86. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
  87. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
  88. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
  89. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
  90. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
  91. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/__init__.py +0 -0
  92. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
  93. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
  94. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
  95. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
  96. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/ref_font/__init__.py +0 -0
  97. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Modeling_Tool/ref_font/simsun.ttc +0 -0
  98. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/README.md +0 -0
  99. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Report/Report_Tool.py +0 -0
  100. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/Report/__init__.py +0 -0
  101. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/SuperModelingFactory.egg-info/SOURCES.txt +0 -0
  102. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
  103. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
  104. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/SuperModelingFactory.egg-info/requires.txt +0 -0
  105. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/SuperModelingFactory.egg-info/top_level.txt +0 -0
  106. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/requirements.txt +0 -0
  107. {supermodelingfactory-0.3.11 → supermodelingfactory-0.3.12}/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,
@@ -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.12"
5
5
 
6
6
  # Core module - base data structures
7
7
  from .Core import (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SuperModelingFactory
3
- Version: 0.3.11
3
+ Version: 0.3.12
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.12
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
@@ -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.12"
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.12"),
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",