SuperModelingFactory 0.3.9__tar.gz → 0.3.10__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.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/Feature_Screen.py +4 -2
  2. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/__init__.py +3 -0
  3. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/feature_validation.py +168 -0
  4. supermodelingfactory-0.3.10/Modeling_Tool/Pipeline/screening_artifact.py +123 -0
  5. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/__init__.py +2 -1
  6. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/PKG-INFO +1 -1
  7. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/SuperModelingFactory.egg-info/PKG-INFO +1 -1
  8. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/SuperModelingFactory.egg-info/SOURCES.txt +1 -0
  9. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/pyproject.toml +1 -1
  10. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/setup.py +1 -1
  11. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/ExcelMaster/ExcelFormatTool.py +0 -0
  12. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/ExcelMaster/ExcelMaster.py +0 -0
  13. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/ExcelMaster/Template.py +0 -0
  14. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/ExcelMaster/Utility.py +0 -0
  15. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/ExcelMaster/__init__.py +0 -0
  16. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/LICENSE +0 -0
  17. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/MANIFEST.in +0 -0
  18. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Binning_Tool.py +0 -0
  19. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
  20. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
  21. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
  22. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
  23. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
  24. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
  25. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
  26. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Proc_Compare.py +0 -0
  27. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Slope_Tool.py +0 -0
  28. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
  29. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
  30. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
  31. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/__init__.py +0 -0
  32. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/kDataFrame.py +0 -0
  33. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
  34. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
  35. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Core/utils.py +0 -0
  36. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
  37. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
  38. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
  39. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
  40. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/__init__.py +0 -0
  41. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/evaluate_model.py +0 -0
  42. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
  43. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
  44. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
  45. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
  46. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Explainability/__init__.py +0 -0
  47. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
  48. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
  49. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
  50. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
  51. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
  52. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
  53. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
  54. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/Weighted_Screen.py +0 -0
  55. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Feature/__init__.py +0 -0
  56. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/Backward_Tool.py +0 -0
  57. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
  58. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
  59. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/GBM_Tool.py +0 -0
  60. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
  61. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/LRM_Tool.py +0 -0
  62. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
  63. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Model/__init__.py +0 -0
  64. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/_common.py +0 -0
  65. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/credit_model.py +0 -0
  66. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
  67. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
  68. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
  69. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
  70. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
  71. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
  72. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
  73. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
  74. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
  75. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Sample/Sample_Split.py +0 -0
  76. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
  77. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/Sample/__init__.py +0 -0
  78. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
  79. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/UAT/__init__.py +0 -0
  80. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
  81. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
  82. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Master.py +0 -0
  83. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
  84. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
  85. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
  86. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
  87. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
  88. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
  89. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
  90. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
  91. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
  92. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/__init__.py +0 -0
  93. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
  94. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
  95. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
  96. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
  97. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/ref_font/__init__.py +0 -0
  98. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Modeling_Tool/ref_font/simsun.ttc +0 -0
  99. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/README.md +0 -0
  100. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Report/Report_Tool.py +0 -0
  101. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/Report/__init__.py +0 -0
  102. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
  103. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
  104. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/SuperModelingFactory.egg-info/requires.txt +0 -0
  105. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/SuperModelingFactory.egg-info/top_level.txt +0 -0
  106. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/requirements.txt +0 -0
  107. {supermodelingfactory-0.3.9 → supermodelingfactory-0.3.10}/setup.cfg +0 -0
@@ -10,8 +10,6 @@ from typing import Any, Mapping
10
10
  import numpy as np
11
11
  import pandas as pd
12
12
 
13
- from Modeling_Tool.Pipeline._common import apply_woe_fit_query, as_list
14
-
15
13
  from .Weighted_Screen import (
16
14
  WeightedScreenResult,
17
15
  _legacy_unweighted_screen,
@@ -123,6 +121,8 @@ def fit_screening_woe_engine(
123
121
  categorical_features: list[str] | None = None,
124
122
  ) -> Any:
125
123
  """Fit a WOE engine on INS for screening steps that reuse bin boundaries."""
124
+ from Modeling_Tool.Pipeline._common import apply_woe_fit_query
125
+
126
126
  fit_ins, _ = apply_woe_fit_query(train, woe_fit_query, target=target_col)
127
127
  if woe_engine.lower() == "monotone":
128
128
  from Modeling_Tool import MonotoneWOEBinner
@@ -130,6 +130,8 @@ def fit_screening_woe_engine(
130
130
  params = dict(monotone_woe_params or {})
131
131
  init_params = {k: v for k, v in params.items() if k in _MONOTONE_INIT_KEYS}
132
132
  fit_params = {k: v for k, v in params.items() if k in _MONOTONE_FIT_KEYS}
133
+ from Modeling_Tool.Pipeline._common import as_list
134
+
133
135
  categorical = [col for col in as_list(categorical_features) if col in features]
134
136
  numeric = [col for col in features if col not in set(categorical)]
135
137
  binner = MonotoneWOEBinner(
@@ -29,6 +29,7 @@ from .feature_validation import (
29
29
  FeatureValidationPipelineConfig,
30
30
  FeatureValidationPipelineResult,
31
31
  )
32
+ from .screening_artifact import FeatureScreeningArtifact, screen_result_to_summary
32
33
 
33
34
  __all__ = [
34
35
  "RejectInferencePipeline",
@@ -52,4 +53,6 @@ __all__ = [
52
53
  "FeatureValidationPipeline",
53
54
  "FeatureValidationPipelineConfig",
54
55
  "FeatureValidationPipelineResult",
56
+ "FeatureScreeningArtifact",
57
+ "screen_result_to_summary",
55
58
  ]
@@ -97,6 +97,10 @@ class FeatureValidationPipelineConfig:
97
97
  }
98
98
  )
99
99
 
100
+ selection_enabled: bool = False
101
+ selection_params: dict[str, Any] = field(default_factory=dict)
102
+ weight_col: str | None = None
103
+
100
104
  write_outputs: bool = True
101
105
  write_excel: bool = True
102
106
 
@@ -117,6 +121,9 @@ class FeatureValidationPipelineResult:
117
121
  report_path: str | None = None
118
122
  batch_metadata: pd.DataFrame | None = None
119
123
  batch_results: dict[str, Any] = field(default_factory=dict)
124
+ selected_features: list[str] = field(default_factory=list)
125
+ selection_summary: dict[str, Any] = field(default_factory=dict)
126
+ screening_artifact: Any | None = None
120
127
 
121
128
 
122
129
  class FeatureValidationPipeline:
@@ -190,6 +197,17 @@ class FeatureValidationPipeline:
190
197
  if cfg.corr_enabled
191
198
  else (pd.DataFrame(), pd.DataFrame(), pd.DataFrame())
192
199
  )
200
+ selected_features: list[str] = []
201
+ selection_summary: dict[str, Any] = {}
202
+ screening_artifact = None
203
+ if cfg.selection_enabled and target_cols:
204
+ selected_features, selection_summary, screening_artifact = self._run_selection(
205
+ splits=splits,
206
+ new_features=new_features,
207
+ incumbent_features=incumbent_features,
208
+ target_cols=target_cols,
209
+ woe_artifacts=woe_artifacts,
210
+ )
193
211
  validation_summary = self._build_validation_summary(
194
212
  data=combined,
195
213
  new_features=new_features,
@@ -201,6 +219,7 @@ class FeatureValidationPipeline:
201
219
  ivks_summary=ivks_summary,
202
220
  high_corr_pairs=high_corr_pairs,
203
221
  woe_artifacts=woe_artifacts,
222
+ selected_features=selected_features,
204
223
  )
205
224
 
206
225
  tables = self._collect_tables(
@@ -213,6 +232,8 @@ class FeatureValidationPipeline:
213
232
  high_corr_pairs,
214
233
  correlated_detail,
215
234
  validation_summary,
235
+ selected_features=selected_features,
236
+ selection_summary=selection_summary,
216
237
  )
217
238
  output_paths, report_path = self._write_outputs(tables)
218
239
 
@@ -229,6 +250,9 @@ class FeatureValidationPipeline:
229
250
  validation_summary=validation_summary,
230
251
  output_paths=output_paths,
231
252
  report_path=report_path,
253
+ selected_features=selected_features,
254
+ selection_summary=selection_summary,
255
+ screening_artifact=screening_artifact,
232
256
  )
233
257
 
234
258
  def _resolve_input_type(self, data: pd.DataFrame | str | Path) -> str:
@@ -354,6 +378,9 @@ class FeatureValidationPipeline:
354
378
  report_path=result.report_path,
355
379
  batch_metadata=result.batch_metadata,
356
380
  batch_results=result.batch_results,
381
+ selected_features=list(result.selected_features),
382
+ selection_summary=dict(result.selection_summary or {}),
383
+ screening_artifact=result.screening_artifact,
357
384
  )
358
385
 
359
386
  def _validate_batch_config(self, new_features: list[str]) -> None:
@@ -498,6 +525,35 @@ class FeatureValidationPipeline:
498
525
  woe_artifacts=woe_artifacts,
499
526
  batch_metadata=batch_metadata,
500
527
  )
528
+ selected_features = self._dedupe(
529
+ [feat for res in batch_results for feat in as_list(res.selected_features)]
530
+ )
531
+ selection_summary: dict[str, Any] = {}
532
+ screening_artifact = None
533
+ if self.config.selection_enabled and target_cols:
534
+ selection_summary = {
535
+ "initial_features": list(new_features),
536
+ "final_features": selected_features,
537
+ "target_col": target_cols[0],
538
+ "config_snapshot": {
539
+ "selection_enabled": True,
540
+ "selection_params": dict(self.config.selection_params or {}),
541
+ "weight_col": self.config.weight_col,
542
+ "batch_mode": True,
543
+ },
544
+ }
545
+ if selected_features:
546
+ from .screening_artifact import FeatureScreeningArtifact
547
+
548
+ screening_artifact = FeatureScreeningArtifact(
549
+ selected_features=selected_features,
550
+ selection_summary=selection_summary,
551
+ woe_artifacts=woe_artifacts,
552
+ source="fvp",
553
+ target_col=target_cols[0],
554
+ weight_col=self.config.weight_col,
555
+ config_snapshot=selection_summary["config_snapshot"],
556
+ )
501
557
  tables = self._collect_tables(
502
558
  feature_sources,
503
559
  distribution_summary,
@@ -508,6 +564,8 @@ class FeatureValidationPipeline:
508
564
  high_corr_pairs,
509
565
  correlated_detail,
510
566
  validation_summary,
567
+ selected_features=selected_features,
568
+ selection_summary=selection_summary,
511
569
  )
512
570
  tables["batch_metadata"] = batch_metadata
513
571
  output_paths, report_path = self._write_outputs(tables)
@@ -532,6 +590,9 @@ class FeatureValidationPipeline:
532
590
  }
533
591
  for idx, res in enumerate(batch_results)
534
592
  },
593
+ selected_features=selected_features,
594
+ selection_summary=selection_summary,
595
+ screening_artifact=screening_artifact,
535
596
  )
536
597
 
537
598
  @staticmethod
@@ -1106,6 +1167,102 @@ class FeatureValidationPipeline:
1106
1167
  except Exception:
1107
1168
  return
1108
1169
 
1170
+ def _build_selection_config(self) -> Any:
1171
+ from Modeling_Tool.Feature.Feature_Screen import screen_config_from_mapping
1172
+
1173
+ cfg = self.config
1174
+ params = dict(cfg.selection_params or {})
1175
+ corr_params = dict(cfg.corr_params or {})
1176
+ psi_params = dict(cfg.psi_params or {})
1177
+ mapping = {
1178
+ "psi_enabled": params.get("psi_enabled", cfg.psi_enabled),
1179
+ "psi_threshold": params.get("psi_threshold", 0.2),
1180
+ "psi_compare_splits": params.get("psi_compare_splits", ["oos"]),
1181
+ "psi_buckets": params.get("psi_buckets", psi_params.get("buckets", 10)),
1182
+ "psi_use_woe_bins": params.get("psi_use_woe_bins", cfg.psi_use_woe_bins),
1183
+ "iv_enabled": params.get("iv_enabled", cfg.ivks_enabled),
1184
+ "iv_threshold": params.get("iv_threshold", 0.02),
1185
+ "iv_nbins": params.get("iv_nbins", psi_params.get("buckets", 10)),
1186
+ "iv_min_bin_prop": params.get("iv_min_bin_prop", psi_params.get("min_bin_prop", 0.05)),
1187
+ "iv_equal_freq": params.get("iv_equal_freq", psi_params.get("equal_freq", True)),
1188
+ "iv_use_woe_bins": params.get("iv_use_woe_bins", cfg.ivks_use_woe_bins),
1189
+ "corr_enabled": params.get("corr_enabled", cfg.corr_enabled),
1190
+ "corr_threshold": params.get(
1191
+ "corr_threshold",
1192
+ params.get("corr_cutpoint", corr_params.get("corr_cutpoint", 0.75)),
1193
+ ),
1194
+ "corr_max_iterations": params.get(
1195
+ "corr_max_iterations",
1196
+ corr_params.get("max_iterations", 10),
1197
+ ),
1198
+ "corr_use_woe_bins": params.get("corr_use_woe_bins", cfg.corr_use_woe_bins),
1199
+ }
1200
+ return screen_config_from_mapping(
1201
+ mapping,
1202
+ woe_engine=cfg.woe_engine,
1203
+ woe_fit_query=cfg.woe_fit_query,
1204
+ woe_params=cfg.woe_params,
1205
+ monotone_woe_params=cfg.monotone_woe_params,
1206
+ )
1207
+
1208
+ def _run_selection(
1209
+ self,
1210
+ splits: dict[str, pd.DataFrame],
1211
+ new_features: list[str],
1212
+ incumbent_features: list[str],
1213
+ target_cols: list[str],
1214
+ woe_artifacts: dict[str, Any],
1215
+ ) -> tuple[list[str], dict[str, Any], Any | None]:
1216
+ from Modeling_Tool.Feature.Feature_Screen import feature_screen
1217
+ from .screening_artifact import FeatureScreeningArtifact, screen_result_to_summary
1218
+
1219
+ cfg = self.config
1220
+ target = target_cols[0]
1221
+ screen_cfg = self._build_selection_config()
1222
+ engine = None
1223
+ if woe_artifacts:
1224
+ engine = woe_artifacts.get("by_target", {}).get(target, {}).get("engine")
1225
+
1226
+ screen_features = list(new_features)
1227
+ if cfg.weight_col and cfg.weight_col not in splits["ins"].columns:
1228
+ raise KeyError(f"Missing weight_col {cfg.weight_col!r} for weighted selection.")
1229
+
1230
+ result = feature_screen(
1231
+ splits,
1232
+ screen_features,
1233
+ target,
1234
+ weight_col=cfg.weight_col,
1235
+ config=screen_cfg,
1236
+ prefit_woe_engine=engine,
1237
+ )
1238
+ config_snapshot = {
1239
+ "selection_enabled": True,
1240
+ "selection_params": dict(cfg.selection_params or {}),
1241
+ "weight_col": cfg.weight_col,
1242
+ "psi_use_woe_bins": screen_cfg.psi_use_woe_bins,
1243
+ "iv_use_woe_bins": screen_cfg.iv_use_woe_bins,
1244
+ "corr_use_woe_bins": screen_cfg.corr_use_woe_bins,
1245
+ "n_incumbent_features": len(incumbent_features),
1246
+ }
1247
+ selection_summary = {
1248
+ "initial_features": list(new_features),
1249
+ "final_features": list(result.selected_features),
1250
+ "target_col": target,
1251
+ "config_snapshot": config_snapshot,
1252
+ }
1253
+ selection_summary.update(screen_result_to_summary(result, new_features))
1254
+ selection_summary["config_snapshot"] = config_snapshot
1255
+ artifact = FeatureScreeningArtifact.from_screen_result(
1256
+ result,
1257
+ initial_features=list(new_features),
1258
+ target_col=target,
1259
+ weight_col=cfg.weight_col,
1260
+ woe_artifacts=woe_artifacts,
1261
+ source="fvp",
1262
+ config_snapshot=config_snapshot,
1263
+ )
1264
+ return list(result.selected_features), selection_summary, artifact
1265
+
1109
1266
  def _run_psi(
1110
1267
  self,
1111
1268
  combined: pd.DataFrame,
@@ -1469,6 +1626,7 @@ class FeatureValidationPipeline:
1469
1626
  ivks_summary: pd.DataFrame,
1470
1627
  high_corr_pairs: pd.DataFrame,
1471
1628
  woe_artifacts: dict[str, Any],
1629
+ selected_features: list[str] | None = None,
1472
1630
  ) -> pd.DataFrame:
1473
1631
  rows = [
1474
1632
  {"metric": "n_rows", "value": len(data)},
@@ -1483,6 +1641,9 @@ class FeatureValidationPipeline:
1483
1641
  {"metric": "woe_targets", "value": len(woe_artifacts.get("by_target", {})) if woe_artifacts else 0},
1484
1642
  {"metric": "feature_sources", "value": feature_sources["feature_source"].value_counts().to_dict()},
1485
1643
  ]
1644
+ if self.config.selection_enabled:
1645
+ rows.append({"metric": "selection_enabled", "value": True})
1646
+ rows.append({"metric": "n_selected_features", "value": len(selected_features or [])})
1486
1647
  return pd.DataFrame(rows)
1487
1648
 
1488
1649
  def _collect_tables(
@@ -1496,6 +1657,8 @@ class FeatureValidationPipeline:
1496
1657
  high_corr_pairs: pd.DataFrame,
1497
1658
  correlated_detail: pd.DataFrame,
1498
1659
  validation_summary: pd.DataFrame,
1660
+ selected_features: list[str] | None = None,
1661
+ selection_summary: dict[str, Any] | None = None,
1499
1662
  ) -> dict[str, pd.DataFrame]:
1500
1663
  tables = {
1501
1664
  "validation_summary": validation_summary,
@@ -1508,6 +1671,11 @@ class FeatureValidationPipeline:
1508
1671
  "high_corr_pairs": high_corr_pairs,
1509
1672
  "correlated_detail": correlated_detail,
1510
1673
  }
1674
+ if selected_features:
1675
+ tables["selected_features"] = pd.DataFrame({"feature": selected_features})
1676
+ screen_summary = (selection_summary or {}).get("screen_summary")
1677
+ if isinstance(screen_summary, pd.DataFrame) and not screen_summary.empty:
1678
+ tables["selection_screen_summary"] = screen_summary
1511
1679
  for name, df in distribution_summary.items():
1512
1680
  tables[f"distribution_{name}"] = df
1513
1681
  return tables
@@ -0,0 +1,123 @@
1
+ """Handoff contract between feature validation and credit modeling pipelines."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import asdict, dataclass, field
6
+ from datetime import datetime, timezone
7
+ from typing import Any, Literal
8
+
9
+ import pandas as pd
10
+
11
+ from Modeling_Tool.Feature.Weighted_Screen import WeightedScreenResult
12
+
13
+
14
+ def screen_result_to_summary(
15
+ result: WeightedScreenResult,
16
+ initial_features: list[str],
17
+ ) -> dict[str, Any]:
18
+ """Convert a screening result into CM-compatible ``feature_selection_summary``."""
19
+ summary: dict[str, Any] = {"initial_features": list(initial_features)}
20
+ if not result.psi_table.empty:
21
+ psi = result.psi_table.copy()
22
+ if "psi_ins_oos" in psi.columns:
23
+ psi["psi"] = psi["psi_ins_oos"]
24
+ elif "psi_max" in psi.columns:
25
+ psi["psi"] = psi["psi_max"]
26
+ summary["psi"] = psi
27
+ if not result.iv_table.empty:
28
+ iv = result.iv_table.copy()
29
+ if "iv_weighted" in iv.columns:
30
+ iv = iv.rename(columns={"iv_weighted": "iv"})
31
+ summary["iv"] = iv
32
+ if not result.corr_dropped.empty:
33
+ summary["corr_dropped"] = result.corr_dropped
34
+ summary["corr_features"] = list(result.selected_features)
35
+ summary["screen_summary"] = result.summary
36
+ summary["final_features"] = list(result.selected_features)
37
+ return summary
38
+
39
+
40
+ @dataclass
41
+ class FeatureScreeningArtifact:
42
+ selected_features: list[str]
43
+ selection_summary: dict[str, Any]
44
+ woe_artifacts: dict[str, Any] | None
45
+ source: Literal["fvp", "cm", "standalone"]
46
+ target_col: str
47
+ weight_col: str | None
48
+ config_snapshot: dict[str, Any] = field(default_factory=dict)
49
+ created_at: str | None = None
50
+
51
+ @classmethod
52
+ def from_screen_result(
53
+ cls,
54
+ result: WeightedScreenResult,
55
+ *,
56
+ initial_features: list[str],
57
+ target_col: str,
58
+ weight_col: str | None,
59
+ woe_artifacts: dict[str, Any] | None,
60
+ source: Literal["fvp", "cm", "standalone"],
61
+ config_snapshot: dict[str, Any] | None = None,
62
+ ) -> FeatureScreeningArtifact:
63
+ return cls(
64
+ selected_features=list(result.selected_features),
65
+ selection_summary=screen_result_to_summary(result, initial_features),
66
+ woe_artifacts=woe_artifacts,
67
+ source=source,
68
+ target_col=target_col,
69
+ weight_col=weight_col,
70
+ config_snapshot=dict(config_snapshot or {}),
71
+ created_at=datetime.now(timezone.utc).isoformat(),
72
+ )
73
+
74
+ @classmethod
75
+ def from_fvp_result(
76
+ cls,
77
+ result: Any,
78
+ *,
79
+ target_col: str | None = None,
80
+ ) -> FeatureScreeningArtifact:
81
+ if getattr(result, "screening_artifact", None) is not None:
82
+ return result.screening_artifact
83
+ resolved_target = target_col
84
+ if not resolved_target:
85
+ summary = getattr(result, "selection_summary", {}) or {}
86
+ resolved_target = summary.get("target_col")
87
+ if not resolved_target:
88
+ raise ValueError("target_col is required when building artifact from FVP result.")
89
+ summary = dict(getattr(result, "selection_summary", {}) or {})
90
+ config_snapshot = dict(summary.get("config_snapshot", {}) or {})
91
+ return cls(
92
+ selected_features=list(getattr(result, "selected_features", []) or []),
93
+ selection_summary=summary,
94
+ woe_artifacts=getattr(result, "woe_artifacts", None),
95
+ source="fvp",
96
+ target_col=str(resolved_target),
97
+ weight_col=config_snapshot.get("weight_col"),
98
+ config_snapshot=config_snapshot,
99
+ created_at=datetime.now(timezone.utc).isoformat(),
100
+ )
101
+
102
+ def validate_for_cm(self, *, target_col: str, weight_col: str | None = None) -> None:
103
+ if self.target_col != target_col:
104
+ raise ValueError(
105
+ f"screening artifact target_col {self.target_col!r} does not match CM target_col {target_col!r}"
106
+ )
107
+ if self.weight_col != weight_col:
108
+ raise ValueError(
109
+ f"screening artifact weight_col {self.weight_col!r} does not match CM weight_col {weight_col!r}"
110
+ )
111
+
112
+ def to_dict(self) -> dict[str, Any]:
113
+ payload = asdict(self)
114
+ for key, value in list(payload.items()):
115
+ if isinstance(value, pd.DataFrame):
116
+ payload[key] = value.to_dict(orient="records")
117
+ return payload
118
+
119
+
120
+ __all__ = [
121
+ "FeatureScreeningArtifact",
122
+ "screen_result_to_summary",
123
+ ]
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  __author__ = "Jingkai Sun"
4
- __version__ = "0.3.9"
4
+ __version__ = "0.3.10"
5
5
 
6
6
  # Core module - base data structures
7
7
  from .Core import (
@@ -116,6 +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
120
  })
120
121
 
121
122
  def __getattr__(name):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SuperModelingFactory
3
- Version: 0.3.9
3
+ Version: 0.3.10
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.9
3
+ Version: 0.3.10
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
@@ -66,6 +66,7 @@ Modeling_Tool/Pipeline/reject_inference.py
66
66
  Modeling_Tool/Pipeline/sample_analysis.py
67
67
  Modeling_Tool/Pipeline/score_comparison.py
68
68
  Modeling_Tool/Pipeline/score_consistency_uat.py
69
+ Modeling_Tool/Pipeline/screening_artifact.py
69
70
  Modeling_Tool/Sample/Distribution_Adaptation.py
70
71
  Modeling_Tool/Sample/Distribution_Adaptation.pyi
71
72
  Modeling_Tool/Sample/Reject_Infer.py
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
12
12
 
13
13
  [project]
14
14
  name = "SuperModelingFactory"
15
- version = "0.3.9"
15
+ version = "0.3.10"
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.9"),
18
+ version=os.environ.get("SMF_VERSION", "0.3.10"),
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",