SuperModelingFactory 0.3.5__tar.gz → 0.3.6__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 (104) hide show
  1. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/_common.py +108 -0
  2. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/credit_model.py +73 -5
  3. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/feature_validation.py +17 -1
  4. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/__init__.py +1 -1
  5. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/PKG-INFO +7 -4
  6. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/README.md +6 -3
  7. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/SuperModelingFactory.egg-info/PKG-INFO +7 -4
  8. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/pyproject.toml +1 -1
  9. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/setup.py +1 -1
  10. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/ExcelMaster/ExcelFormatTool.py +0 -0
  11. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/ExcelMaster/ExcelMaster.py +0 -0
  12. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/ExcelMaster/Template.py +0 -0
  13. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/ExcelMaster/Utility.py +0 -0
  14. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/ExcelMaster/__init__.py +0 -0
  15. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/LICENSE +0 -0
  16. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/MANIFEST.in +0 -0
  17. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Binning_Tool.py +0 -0
  18. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
  19. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
  20. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
  21. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
  22. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
  23. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
  24. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
  25. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Proc_Compare.py +0 -0
  26. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Slope_Tool.py +0 -0
  27. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
  28. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
  29. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
  30. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/__init__.py +0 -0
  31. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/kDataFrame.py +0 -0
  32. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
  33. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
  34. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Core/utils.py +0 -0
  35. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
  36. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
  37. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/Model_Eval_Tool.py +0 -0
  38. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
  39. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/__init__.py +0 -0
  40. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/evaluate_model.py +0 -0
  41. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
  42. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
  43. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
  44. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
  45. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Explainability/__init__.py +0 -0
  46. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
  47. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
  48. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
  49. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
  50. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
  51. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
  52. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
  53. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Feature/__init__.py +0 -0
  54. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/Backward_Tool.py +0 -0
  55. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
  56. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
  57. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/GBM_Tool.py +0 -0
  58. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
  59. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/LRM_Tool.py +0 -0
  60. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
  61. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Model/__init__.py +0 -0
  62. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/__init__.py +0 -0
  63. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
  64. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
  65. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
  66. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
  67. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
  68. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
  69. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
  70. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
  71. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
  72. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Sample/Sample_Split.py +0 -0
  73. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
  74. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/Sample/__init__.py +0 -0
  75. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
  76. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/UAT/__init__.py +0 -0
  77. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
  78. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
  79. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Master.py +0 -0
  80. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
  81. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +0 -0
  82. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
  83. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
  84. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
  85. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
  86. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
  87. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
  88. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
  89. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/__init__.py +0 -0
  90. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
  91. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
  92. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
  93. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
  94. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/ref_font/__init__.py +0 -0
  95. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Modeling_Tool/ref_font/simsun.ttc +0 -0
  96. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Report/Report_Tool.py +0 -0
  97. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/Report/__init__.py +0 -0
  98. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/SuperModelingFactory.egg-info/SOURCES.txt +0 -0
  99. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
  100. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
  101. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/SuperModelingFactory.egg-info/requires.txt +0 -0
  102. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/SuperModelingFactory.egg-info/top_level.txt +0 -0
  103. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/requirements.txt +0 -0
  104. {supermodelingfactory-0.3.5 → supermodelingfactory-0.3.6}/setup.cfg +0 -0
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ import re
4
5
  from dataclasses import fields, is_dataclass
5
6
  from pathlib import Path
6
7
  from typing import Any, Iterable, Mapping
@@ -8,6 +9,36 @@ from typing import Any, Iterable, Mapping
8
9
  import numpy as np
9
10
  import pandas as pd
10
11
 
12
+ _QUERY_COLUMN_RE = re.compile(r"(?<!['\"])\b([A-Za-z_][\w]*)\b(?![\'\"])")
13
+ _QUERY_RESERVED = frozenset(
14
+ {
15
+ "and",
16
+ "or",
17
+ "not",
18
+ "True",
19
+ "False",
20
+ "None",
21
+ "in",
22
+ "is",
23
+ "where",
24
+ "if",
25
+ "else",
26
+ "lambda",
27
+ "abs",
28
+ "all",
29
+ "any",
30
+ "max",
31
+ "min",
32
+ "sum",
33
+ "len",
34
+ "round",
35
+ "float",
36
+ "int",
37
+ "str",
38
+ "bool",
39
+ }
40
+ )
41
+
11
42
 
12
43
  def make_dirs(*paths: str | os.PathLike | None) -> None:
13
44
  for path in paths:
@@ -29,6 +60,83 @@ def merge_dict(base: Mapping[str, Any] | None, override: Mapping[str, Any] | Non
29
60
  return merged
30
61
 
31
62
 
63
+ def query_referenced_columns(expr: str) -> set[str]:
64
+ """Return identifier-like column names referenced in a pandas query expression."""
65
+ if not expr or not str(expr).strip():
66
+ return set()
67
+ return {
68
+ match.group(1)
69
+ for match in _QUERY_COLUMN_RE.finditer(str(expr))
70
+ if match.group(1) not in _QUERY_RESERVED
71
+ }
72
+
73
+
74
+ def validate_woe_fit_query_syntax(data: pd.DataFrame, query: str) -> None:
75
+ """Raise ValueError when a woe_fit_query expression is syntactically invalid."""
76
+ if not query:
77
+ return
78
+ sample = data.head(min(100, len(data)))
79
+ if sample.empty:
80
+ raise ValueError("woe_fit_query cannot be validated on an empty dataset.")
81
+ try:
82
+ sample.query(query, engine="python")
83
+ except SyntaxError as exc:
84
+ raise ValueError(f"Invalid woe_fit_query syntax: {query!r}") from exc
85
+ except Exception as exc:
86
+ raise ValueError(f"woe_fit_query failed on sample data: {query!r} ({exc})") from exc
87
+
88
+
89
+ def validate_woe_fit_query_columns(
90
+ query: str,
91
+ available_cols: Iterable[str],
92
+ *,
93
+ context: str = "dataset",
94
+ ) -> None:
95
+ """Raise KeyError when query references columns missing from available_cols."""
96
+ if not query:
97
+ return
98
+ available = set(available_cols)
99
+ missing = sorted(query_referenced_columns(query) - available)
100
+ if missing:
101
+ raise KeyError(
102
+ f"woe_fit_query references missing columns in {context}: {missing}. "
103
+ "Add them to the input data, batch_base_cols, or population_dims."
104
+ )
105
+
106
+
107
+ def apply_woe_fit_query(
108
+ train: pd.DataFrame,
109
+ query: str | None,
110
+ *,
111
+ target: str | None = None,
112
+ ) -> tuple[pd.DataFrame, dict[str, Any] | None]:
113
+ """Filter WOE fit rows via pandas query and return an audit row for refine_summary."""
114
+ if not query:
115
+ return train, None
116
+ n_before = len(train)
117
+ try:
118
+ filtered = train.query(query, engine="python").copy()
119
+ except Exception as exc:
120
+ audit = {
121
+ "target": target,
122
+ "step": "fit_filter",
123
+ "status": "error",
124
+ "query": query,
125
+ "n_before": n_before,
126
+ "error": repr(exc),
127
+ }
128
+ return train, audit
129
+ audit = {
130
+ "target": target,
131
+ "step": "fit_filter",
132
+ "status": "ok",
133
+ "query": query,
134
+ "n_before": n_before,
135
+ "n_after": len(filtered),
136
+ }
137
+ return filtered, audit
138
+
139
+
32
140
  def dataclass_to_dict(obj: Any) -> dict[str, Any]:
33
141
  if not is_dataclass(obj):
34
142
  return dict(obj)
@@ -9,11 +9,14 @@ import pandas as pd
9
9
 
10
10
  from ._common import (
11
11
  add_dataset_with_optional_weight,
12
+ apply_woe_fit_query,
12
13
  as_list,
13
14
  make_dirs,
14
15
  merge_dict,
15
16
  predict_positive,
16
17
  safe_to_csv,
18
+ validate_woe_fit_query_columns,
19
+ validate_woe_fit_query_syntax,
17
20
  write_basic_excel,
18
21
  )
19
22
 
@@ -48,6 +51,8 @@ class CreditModelPipelineConfig:
48
51
  }
49
52
  )
50
53
  woe_engine: str = "equal_freq"
54
+ woe_fit_query: str | None = None
55
+ extra_eval_datasets: dict[str, pd.DataFrame] | None = None
51
56
  woe_params: dict[str, Any] = field(
52
57
  default_factory=lambda: {"nbins": 10, "equal_freq": True, "min_bin_prop": 0.05}
53
58
  )
@@ -208,7 +213,11 @@ class CreditModelPipeline:
208
213
  models = self._train_models(model_inputs)
209
214
 
210
215
  optuna_results = self._run_optuna(model_inputs)
211
- perf_results = self._evaluate_models(model_inputs, models)
216
+ perf_results = self._evaluate_models(
217
+ model_inputs,
218
+ models,
219
+ woe_artifacts.get("extra_eval"),
220
+ )
212
221
  explain_outputs = self._run_explainability(model_inputs, models)
213
222
  model_paths: dict[str, str] = {}
214
223
  artifact_paths: dict[str, str] = {}
@@ -310,6 +319,23 @@ class CreditModelPipeline:
310
319
  raise ValueError("warm_start_score_type must be 'probability' or 'log_odds'")
311
320
  if cfg.warm_start_on_unsupported not in {"skip", "raise"}:
312
321
  raise ValueError("warm_start_on_unsupported must be 'skip' or 'raise'")
322
+ if cfg.woe_fit_query:
323
+ validate_woe_fit_query_columns(cfg.woe_fit_query, data.columns, context="input data")
324
+ validate_woe_fit_query_syntax(data, cfg.woe_fit_query)
325
+ if cfg.extra_eval_datasets:
326
+ reserved = {"ins", "oos", "oot"}
327
+ conflicts = sorted(set(cfg.extra_eval_datasets) & reserved)
328
+ if conflicts:
329
+ raise ValueError(
330
+ f"extra_eval_datasets names cannot conflict with split names {sorted(reserved)}: {conflicts}"
331
+ )
332
+ for name, extra_df in cfg.extra_eval_datasets.items():
333
+ if cfg.target_col not in extra_df.columns:
334
+ raise KeyError(f"extra_eval_datasets[{name!r}] missing target_col {cfg.target_col!r}")
335
+ if cfg.warm_start_enabled and cfg.warm_start_score_col and cfg.warm_start_score_col not in extra_df.columns:
336
+ raise KeyError(
337
+ f"extra_eval_datasets[{name!r}] missing warm_start_score_col {cfg.warm_start_score_col!r}"
338
+ )
313
339
 
314
340
  def _gbm_weight_kwargs(self, train: pd.DataFrame, val: pd.DataFrame) -> dict[str, Any]:
315
341
  cfg = self.config
@@ -466,6 +492,24 @@ class CreditModelPipeline:
466
492
  summary["final_features"] = list(current)
467
493
  return summary, list(current)
468
494
 
495
+ def _transform_extra_eval_datasets(
496
+ self,
497
+ transform_fn: Any,
498
+ feature_cols: list[str],
499
+ *,
500
+ woe_suffix: str | None = None,
501
+ ) -> dict[str, pd.DataFrame]:
502
+ cfg = self.config
503
+ if not cfg.extra_eval_datasets:
504
+ return {}
505
+ transformed: dict[str, pd.DataFrame] = {}
506
+ for name, df in cfg.extra_eval_datasets.items():
507
+ if woe_suffix is not None:
508
+ transformed[name] = transform_fn(df, varlist=feature_cols, suffix=woe_suffix)
509
+ else:
510
+ transformed[name] = transform_fn(df)
511
+ return transformed
512
+
469
513
  def _fit_woe(self, splits: dict[str, pd.DataFrame], feature_cols: list[str]) -> dict[str, Any]:
470
514
  from Modeling_Tool import MonotoneWOEBinner, WOE_Master
471
515
  from Modeling_Tool.WOE.WOE_Adapter import as_woe_engine
@@ -475,6 +519,11 @@ class CreditModelPipeline:
475
519
  woe_suffix = cfg.woe_params.get("woe_suffix", "_woe")
476
520
  graph_dir = str(Path(cfg.output_dir) / "figs" / "woe")
477
521
  woe_features = [f"{col}{woe_suffix}" for col in feature_cols]
522
+ fit_ins, _ = apply_woe_fit_query(
523
+ splits["ins"],
524
+ cfg.woe_fit_query,
525
+ target=cfg.target_col,
526
+ )
478
527
 
479
528
  if cfg.woe_engine.lower() == "monotone":
480
529
  params = merge_dict(
@@ -486,7 +535,7 @@ class CreditModelPipeline:
486
535
  cfg.monotone_woe_params,
487
536
  )
488
537
  binner = MonotoneWOEBinner(**params)
489
- binner.fit(splits["ins"], chi2_binning=bool(cfg.monotone_woe_params.get("chi2_binning", False)))
538
+ binner.fit(fit_ins, chi2_binning=bool(cfg.monotone_woe_params.get("chi2_binning", False)))
490
539
  if cfg.write_outputs and cfg.plot_outputs:
491
540
  binner.plot_woe_graph(graph_path=str(Path(cfg.output_dir) / "figs" / "mono_woe"))
492
541
  adapter = as_woe_engine(binner, woe_suffix=woe_suffix)
@@ -496,9 +545,14 @@ class CreditModelPipeline:
496
545
  }
497
546
  woe_table = adapter.get_woe_table(varlist=feature_cols)
498
547
  engine = adapter
548
+ extra_eval = self._transform_extra_eval_datasets(
549
+ adapter.transform,
550
+ feature_cols,
551
+ woe_suffix=woe_suffix,
552
+ )
499
553
  else:
500
554
  master = WOE_Master(
501
- train_data=splits["ins"],
555
+ train_data=fit_ins,
502
556
  varlist=feature_cols,
503
557
  dep=cfg.target_col,
504
558
  graph_save_dir=graph_dir,
@@ -518,13 +572,18 @@ class CreditModelPipeline:
518
572
  dirname="overall",
519
573
  varlist=feature_cols,
520
574
  )
521
- woe_table = get_overall_woe_table(master, splits["ins"], varlist=feature_cols)
575
+ woe_table = get_overall_woe_table(master, fit_ins, varlist=feature_cols)
522
576
  engine = master
577
+ extra_eval = self._transform_extra_eval_datasets(master.transform, feature_cols)
523
578
 
524
579
  if cfg.warm_start_enabled and cfg.warm_start_score_col:
525
580
  for name, df in woe_splits.items():
526
581
  if cfg.warm_start_score_col not in df.columns:
527
582
  df[cfg.warm_start_score_col] = splits[name][cfg.warm_start_score_col].to_numpy()
583
+ for name, df in extra_eval.items():
584
+ if cfg.warm_start_score_col not in df.columns:
585
+ source = cfg.extra_eval_datasets[name]
586
+ df[cfg.warm_start_score_col] = source[cfg.warm_start_score_col].to_numpy()
528
587
 
529
588
  return {
530
589
  "engine": engine,
@@ -533,6 +592,7 @@ class CreditModelPipeline:
533
592
  "woe_features": woe_features,
534
593
  "woe_suffix": woe_suffix,
535
594
  "splits": woe_splits,
595
+ "extra_eval": extra_eval,
536
596
  "woe_table": woe_table,
537
597
  }
538
598
 
@@ -873,13 +933,20 @@ class CreditModelPipeline:
873
933
  self,
874
934
  model_inputs: dict[str, dict[str, Any]],
875
935
  models: dict[str, tuple[Any, Any, list[str]]],
936
+ extra_eval_splits: dict[str, pd.DataFrame] | None = None,
876
937
  ) -> dict[str, pd.DataFrame]:
877
938
  from Modeling_Tool import PerformanceEvaluator
878
939
 
879
940
  cfg = self.config
880
941
  results = {}
942
+ extra_eval_splits = extra_eval_splits or {}
881
943
  for name, (wrapper, _, feature_cols) in models.items():
882
944
  splits = model_inputs[name]["splits"]
945
+ source = str(model_inputs[name].get("source", "woe")).lower()
946
+ if source == "raw" and cfg.extra_eval_datasets:
947
+ model_extra = {key: df.copy() for key, df in cfg.extra_eval_datasets.items()}
948
+ else:
949
+ model_extra = extra_eval_splits
883
950
  evaluator = PerformanceEvaluator(
884
951
  tgt_name=cfg.target_col,
885
952
  scr_name=f"pred_{name}",
@@ -887,7 +954,8 @@ class CreditModelPipeline:
887
954
  min_bin_prop=cfg.perf_min_bin_prop,
888
955
  equal_freq=True,
889
956
  )
890
- for ds_name, df in splits.items():
957
+ eval_splits = {**splits, **model_extra}
958
+ for ds_name, df in eval_splits.items():
891
959
  scored = df.copy()
892
960
  scored[f"pred_{name}"] = self._predict_model_positive(name, wrapper, scored, feature_cols)
893
961
  add_dataset_with_optional_weight(evaluator, ds_name, scored, weight_col=cfg.weight_col)
@@ -8,7 +8,14 @@ from typing import Any, Literal
8
8
  import numpy as np
9
9
  import pandas as pd
10
10
 
11
- from ._common import as_list, make_dirs, safe_to_csv
11
+ from ._common import (
12
+ apply_woe_fit_query,
13
+ as_list,
14
+ make_dirs,
15
+ safe_to_csv,
16
+ validate_woe_fit_query_columns,
17
+ validate_woe_fit_query_syntax,
18
+ )
12
19
 
13
20
 
14
21
  @dataclass
@@ -47,6 +54,7 @@ class FeatureValidationPipelineConfig:
47
54
  )
48
55
 
49
56
  woe_enabled: bool = True
57
+ woe_fit_query: str | None = None
50
58
  woe_engine: str = "monotone"
51
59
  woe_params: dict[str, Any] = field(
52
60
  default_factory=lambda: {"nbins": 10, "equal_freq": True, "min_bin_prop": 0.05}
@@ -257,6 +265,8 @@ class FeatureValidationPipeline:
257
265
  self._validate_batch_config(new_features)
258
266
  feature_batches = self._make_feature_batches(new_features)
259
267
  base_cols = self._resolve_batch_base_cols(header, incumbent_features, target_cols)
268
+ if cfg.woe_fit_query:
269
+ validate_woe_fit_query_columns(cfg.woe_fit_query, base_cols, context="CSV batch base columns")
260
270
 
261
271
  base_df = self._read_csv(csv_path, usecols=base_cols).copy()
262
272
  base_df["_smf_batch_row_id"] = np.arange(len(base_df))
@@ -777,6 +787,9 @@ class FeatureValidationPipeline:
777
787
  raise ValueError("psi_reference_dataset must be one of ins/oos/oot/external")
778
788
  if cfg.psi_reference_dataset == "external" and cfg.psi_reference_data is None:
779
789
  raise ValueError("psi_reference_data is required when psi_reference_dataset='external'")
790
+ if cfg.woe_fit_query:
791
+ validate_woe_fit_query_columns(cfg.woe_fit_query, data.columns, context="input data")
792
+ validate_woe_fit_query_syntax(data, cfg.woe_fit_query)
780
793
 
781
794
  def _resolve_new_features(self, data: pd.DataFrame) -> list[str]:
782
795
  cfg = self.config
@@ -979,6 +992,9 @@ class FeatureValidationPipeline:
979
992
  refine_rows = []
980
993
  for target in target_cols:
981
994
  train = splits["ins"][splits["ins"][target].notna()].copy()
995
+ train, fit_filter_row = apply_woe_fit_query(train, cfg.woe_fit_query, target=target)
996
+ if fit_filter_row is not None:
997
+ refine_rows.append(fit_filter_row)
982
998
  if len(train) < max(10, cfg.min_group_size):
983
999
  refine_rows.append({"target": target, "step": "fit", "status": "skipped_min_group_size", "n": len(train)})
984
1000
  continue
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  __author__ = "Jingkai Sun"
4
- __version__ = "0.3.5"
4
+ __version__ = "0.3.6"
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.5
3
+ Version: 0.3.6
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
@@ -114,7 +114,10 @@ SuperModelingFactory/
114
114
  │ ├── Feature/ # 特征分析:分布偏移、PSI、相关性过滤
115
115
  │ ├── Model/ # 模型训练:LR、LightGBM、XGBoost、变量选择
116
116
  │ ├── Eval/ # 模型评估:Gains 表、ROC/KS、性能汇总
117
- └── Sample/ # 样本管理:切分、分层、拒绝推断、分布适配
117
+ ├── Sample/ # 样本管理:切分、分层、拒绝推断、分布适配
118
+ │ ├── Explainability/ # 模型解释:SHAP、LIME、PDP、ICE、ALE、Owen
119
+ │ ├── Pipeline/ # 一键流水线:特征验收、信用建模、拒绝推断等
120
+ │ └── UAT/ # 线上线下分数一致性校验
118
121
  ├── ExcelMaster/ # Excel 报告引擎
119
122
  │ ├── ExcelFormatTool.py # 格式定义(50+ 预设单元格格式)
120
123
  │ ├── ExcelMaster.py # 核心引擎(光标流式写入、图表、条件格式)
@@ -262,9 +265,9 @@ em.close_workbook()
262
265
 
263
266
  ## 版本
264
267
 
265
- - **Version**: 0.3.5
268
+ - **Version**: 0.3.6
266
269
  - **Author**: Jingkai Sun
267
270
 
268
271
  ## 许可证
269
272
 
270
- 内部项目,仅供团队使用。
273
+ 本项目采用 **Business Source License 1.1**(Change Date **2030-06-24**)。详见文首 [许可证](#许可证) 一节与 [LICENSE](LICENSE) 全文。
@@ -57,7 +57,10 @@ SuperModelingFactory/
57
57
  │ ├── Feature/ # 特征分析:分布偏移、PSI、相关性过滤
58
58
  │ ├── Model/ # 模型训练:LR、LightGBM、XGBoost、变量选择
59
59
  │ ├── Eval/ # 模型评估:Gains 表、ROC/KS、性能汇总
60
- └── Sample/ # 样本管理:切分、分层、拒绝推断、分布适配
60
+ ├── Sample/ # 样本管理:切分、分层、拒绝推断、分布适配
61
+ │ ├── Explainability/ # 模型解释:SHAP、LIME、PDP、ICE、ALE、Owen
62
+ │ ├── Pipeline/ # 一键流水线:特征验收、信用建模、拒绝推断等
63
+ │ └── UAT/ # 线上线下分数一致性校验
61
64
  ├── ExcelMaster/ # Excel 报告引擎
62
65
  │ ├── ExcelFormatTool.py # 格式定义(50+ 预设单元格格式)
63
66
  │ ├── ExcelMaster.py # 核心引擎(光标流式写入、图表、条件格式)
@@ -205,9 +208,9 @@ em.close_workbook()
205
208
 
206
209
  ## 版本
207
210
 
208
- - **Version**: 0.3.5
211
+ - **Version**: 0.3.6
209
212
  - **Author**: Jingkai Sun
210
213
 
211
214
  ## 许可证
212
215
 
213
- 内部项目,仅供团队使用。
216
+ 本项目采用 **Business Source License 1.1**(Change Date **2030-06-24**)。详见文首 [许可证](#许可证) 一节与 [LICENSE](LICENSE) 全文。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SuperModelingFactory
3
- Version: 0.3.5
3
+ Version: 0.3.6
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
@@ -114,7 +114,10 @@ SuperModelingFactory/
114
114
  │ ├── Feature/ # 特征分析:分布偏移、PSI、相关性过滤
115
115
  │ ├── Model/ # 模型训练:LR、LightGBM、XGBoost、变量选择
116
116
  │ ├── Eval/ # 模型评估:Gains 表、ROC/KS、性能汇总
117
- └── Sample/ # 样本管理:切分、分层、拒绝推断、分布适配
117
+ ├── Sample/ # 样本管理:切分、分层、拒绝推断、分布适配
118
+ │ ├── Explainability/ # 模型解释:SHAP、LIME、PDP、ICE、ALE、Owen
119
+ │ ├── Pipeline/ # 一键流水线:特征验收、信用建模、拒绝推断等
120
+ │ └── UAT/ # 线上线下分数一致性校验
118
121
  ├── ExcelMaster/ # Excel 报告引擎
119
122
  │ ├── ExcelFormatTool.py # 格式定义(50+ 预设单元格格式)
120
123
  │ ├── ExcelMaster.py # 核心引擎(光标流式写入、图表、条件格式)
@@ -262,9 +265,9 @@ em.close_workbook()
262
265
 
263
266
  ## 版本
264
267
 
265
- - **Version**: 0.3.5
268
+ - **Version**: 0.3.6
266
269
  - **Author**: Jingkai Sun
267
270
 
268
271
  ## 许可证
269
272
 
270
- 内部项目,仅供团队使用。
273
+ 本项目采用 **Business Source License 1.1**(Change Date **2030-06-24**)。详见文首 [许可证](#许可证) 一节与 [LICENSE](LICENSE) 全文。
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
12
12
 
13
13
  [project]
14
14
  name = "SuperModelingFactory"
15
- version = "0.3.5"
15
+ version = "0.3.6"
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.5"),
18
+ version=os.environ.get("SMF_VERSION", "0.3.6"),
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",