SuperModelingFactory 0.6.3__tar.gz → 0.6.5__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 (115) hide show
  1. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/Model_Eval_Tool.py +10 -1
  2. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/evaluate_model.py +172 -18
  3. supermodelingfactory-0.6.5/Modeling_Tool/Feature/ODPS_Distribution_Tool.py +602 -0
  4. supermodelingfactory-0.6.5/Modeling_Tool/Feature/ODPS_Distribution_Tool.pyi +23 -0
  5. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/__init__.py +2 -1
  6. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/_common.py +43 -0
  7. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/credit_model.py +275 -21
  8. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/feature_validation.py +26 -2
  9. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/field_meta.py +10 -0
  10. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Monotone_Binner.py +8 -5
  11. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/__init__.py +3 -1
  12. {supermodelingfactory-0.6.3/SuperModelingFactory.egg-info → supermodelingfactory-0.6.5}/PKG-INFO +2 -2
  13. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/README.md +1 -1
  14. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5/SuperModelingFactory.egg-info}/PKG-INFO +2 -2
  15. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/SuperModelingFactory.egg-info/SOURCES.txt +2 -0
  16. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/pyproject.toml +1 -1
  17. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/setup.py +1 -1
  18. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/ExcelMaster/ExcelFormatTool.py +0 -0
  19. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/ExcelMaster/ExcelMaster.py +0 -0
  20. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/ExcelMaster/Template.py +0 -0
  21. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/ExcelMaster/Utility.py +0 -0
  22. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/ExcelMaster/__init__.py +0 -0
  23. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/LICENSE +0 -0
  24. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/MANIFEST.in +0 -0
  25. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Binning_Tool.py +0 -0
  26. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Binning_Tool.pyi +0 -0
  27. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Check_DuckDB_Compatibility.py +0 -0
  28. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Json_Data_Converter.py +0 -0
  29. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Model_Registry_Tool.py +0 -0
  30. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/ODPS_Tool.py +0 -0
  31. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Parallel_Engine.py +0 -0
  32. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Parallel_ODPS_Manager.py +0 -0
  33. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Proc_Compare.py +0 -0
  34. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Slope_Tool.py +0 -0
  35. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/Slope_Tool.pyi +0 -0
  36. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/XOR_Encryptor.py +0 -0
  37. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/XOR_Encryptor.pyi +0 -0
  38. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/__init__.py +0 -0
  39. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/kDataFrame.py +0 -0
  40. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/kDataFrame.pyi +0 -0
  41. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/sample_weight_utils.py +0 -0
  42. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Core/utils.py +0 -0
  43. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/Evaluation_Tool.py +0 -0
  44. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/Evaluation_Tool.pyi +0 -0
  45. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/Model_Eval_Tool.pyi +0 -0
  46. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/__init__.py +0 -0
  47. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/evaluate_model.pyi +0 -0
  48. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Eval/weighted_eval_utils.py +0 -0
  49. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Explainability/Coalition_Structure.py +0 -0
  50. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Explainability/Model_Explainer.py +0 -0
  51. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Explainability/__init__.py +0 -0
  52. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/Distribution_Tool.py +0 -0
  53. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/Distribution_Tool.pyi +0 -0
  54. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/Feature_Insights.py +0 -0
  55. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/Feature_Insights.pyi +0 -0
  56. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/Feature_Screen.py +0 -0
  57. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/PSI_Tool.py +0 -0
  58. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/PSI_Tool.pyi +0 -0
  59. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +0 -0
  60. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Feature/Weighted_Screen.py +0 -0
  61. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/Backward_Tool.py +0 -0
  62. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/Backward_Tool.pyi +0 -0
  63. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/GBM_Search_Tool.py +0 -0
  64. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/GBM_Tool.py +0 -0
  65. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/GBM_Tool.pyi +0 -0
  66. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/LRM_Tool.py +0 -0
  67. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/LRM_Tool.pyi +0 -0
  68. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Model/__init__.py +0 -0
  69. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/__init__.py +0 -0
  70. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/mock_sample.py +0 -0
  71. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/orchestrator.py +0 -0
  72. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/reject_inference.py +0 -0
  73. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/sample_analysis.py +0 -0
  74. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/score_comparison.py +0 -0
  75. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/score_consistency_uat.py +0 -0
  76. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Pipeline/screening_artifact.py +0 -0
  77. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Sample/Distribution_Adaptation.py +0 -0
  78. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Sample/Distribution_Adaptation.pyi +0 -0
  79. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Sample/Reject_Infer.py +0 -0
  80. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Sample/Reject_Infer.pyi +0 -0
  81. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Sample/Sample_Split.py +0 -0
  82. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Sample/Sample_Split.pyi +0 -0
  83. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/Sample/__init__.py +0 -0
  84. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/UAT/UAT_Consistency_Checker.py +0 -0
  85. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/UAT/__init__.py +0 -0
  86. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Adapter.py +0 -0
  87. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Adapter.pyi +0 -0
  88. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Master.py +0 -0
  89. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Master.pyi +0 -0
  90. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +0 -0
  91. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Plot_Tool.py +0 -0
  92. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Plot_Tool.pyi +0 -0
  93. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Report_Builder.py +0 -0
  94. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Report_Builder.pyi +0 -0
  95. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Tool.py +0 -0
  96. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/WOE_Tool.pyi +0 -0
  97. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/__init__.py +0 -0
  98. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/plot_woe_tool.py +0 -0
  99. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/WOE/plot_woe_tool.pyi +0 -0
  100. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/_utils/__init__.py +0 -0
  101. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/_utils/nan_guard.py +0 -0
  102. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/_utils/robust.py +0 -0
  103. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/_utils/sentinels.py +0 -0
  104. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/ref_font/KaiTi.ttf +0 -0
  105. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/ref_font/WeiRuanYaHei.ttf +0 -0
  106. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/ref_font/__init__.py +0 -0
  107. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Modeling_Tool/ref_font/simsun.ttc +0 -0
  108. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Report/Report_Tool.py +0 -0
  109. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/Report/__init__.py +0 -0
  110. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/SuperModelingFactory.egg-info/dependency_links.txt +0 -0
  111. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/SuperModelingFactory.egg-info/not-zip-safe +0 -0
  112. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/SuperModelingFactory.egg-info/requires.txt +0 -0
  113. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/SuperModelingFactory.egg-info/top_level.txt +0 -0
  114. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/requirements.txt +0 -0
  115. {supermodelingfactory-0.6.3 → supermodelingfactory-0.6.5}/setup.cfg +0 -0
@@ -2362,15 +2362,24 @@ class PerformanceEvaluator:
2362
2362
  for name, data in self.datasets.items():
2363
2363
  if data is None:
2364
2364
  continue
2365
+ wc = self.dataset_weight_cols.get(name) or active_weight_col
2365
2366
  work_data = data.copy()
2366
2367
  scr_name = self.scr_name
2367
2368
  if scr_name is None:
2368
2369
  scr_name = "_mdl_scr"
2369
2370
  work_data[scr_name] = self.model.predict_proba(work_data.loc[:, self.feature_cols])[:, 1]
2370
- eval_datasets[name] = {
2371
+ payload = {
2371
2372
  "y_true": work_data[self.tgt_name],
2372
2373
  "y_score": work_data[scr_name],
2373
2374
  }
2375
+ resolved_weight = _weighted_eval.resolve_weights(
2376
+ work_data,
2377
+ weight_col=wc,
2378
+ expected_len=len(work_data),
2379
+ )
2380
+ if resolved_weight is not None:
2381
+ payload["sample_weight"] = resolved_weight
2382
+ eval_datasets[name] = payload
2374
2383
  if eval_datasets:
2375
2384
  evaluate_performance(
2376
2385
  datasets=eval_datasets,
@@ -554,7 +554,49 @@ def __plot_kde_axes_base(ax, fontdicts):
554
554
  ax.set_ylabel('Density', fontdict=fontdicts['axislabel'])
555
555
 
556
556
 
557
- def __plot_single_kde_axes(y_true, y_score, bins, ax, fontdicts):
557
+ def _plot_weighted_kde_line(values, weights, ax, color, label):
558
+ values = np.asarray(values, dtype=float)
559
+ weights = np.asarray(weights, dtype=float)
560
+ mask = np.isfinite(values) & np.isfinite(weights) & (weights > 0)
561
+ values = values[mask]
562
+ weights = weights[mask]
563
+ if values.size == 0:
564
+ return
565
+ if values.size < 2 or np.unique(values).size < 2:
566
+ ax.axvline(
567
+ np.average(values, weights=weights),
568
+ color=color,
569
+ linewidth=1.5,
570
+ label=label,
571
+ )
572
+ return
573
+
574
+ try:
575
+ sns.kdeplot(
576
+ x=values,
577
+ weights=weights,
578
+ ax=ax,
579
+ color=color,
580
+ label=label,
581
+ bw_adjust=0.5,
582
+ common_norm=False,
583
+ warn_singular=False,
584
+ )
585
+ except (TypeError, ValueError, np.linalg.LinAlgError):
586
+ # Older seaborn/scipy combinations may reject weighted KDE. A
587
+ # weighted density line still preserves the intended population.
588
+ n_bins = max(5, min(50, int(np.sqrt(values.size))))
589
+ density, edges = np.histogram(
590
+ values,
591
+ bins=n_bins,
592
+ weights=weights,
593
+ density=True,
594
+ )
595
+ centers = (edges[:-1] + edges[1:]) / 2.0
596
+ ax.plot(centers, density, color=color, linewidth=1.5, label=label)
597
+
598
+
599
+ def __plot_single_kde_axes(y_true, y_score, bins, ax, fontdicts, sample_weight=None):
558
600
  """在axes上绘制单个kde图.
559
601
 
560
602
  Parameters
@@ -572,16 +614,56 @@ def __plot_single_kde_axes(y_true, y_score, bins, ax, fontdicts):
572
614
  """
573
615
  __plot_kde_axes_base(ax, fontdicts)
574
616
 
575
- sns.distplot(y_score, bins=bins, hist=True, kde=False, ax=ax,
576
- hist_kws={'density': True, 'rwidth': 0.95, 'color': palette['ClassicBlueRedGrey'][2], 'alpha': 1, 'label': 'Total'}, )
577
- sns.distplot(y_score[np.where(y_true==0)], bins=bins, hist=False, kde=True,
578
- kde_kws={'bw': 1/bins/2, 'color': palette['ClassicBlueRedGrey'][0], 'label': 'Neg KDE'}, )
579
- sns.distplot(y_score[np.where(y_true==1)], hist=False, kde=True,
580
- kde_kws={'bw': 1/bins/2, 'color': palette['ClassicBlueRedGrey'][1], 'label': 'Pos KDE'}, )
581
- ax.axvline(x=np.mean(y_true), linestyle='-', linewidth=1, color=palette['ClassicGreyRed'][0],label='True')
582
- ax.axvline(x=np.mean(y_score), linestyle='--', linewidth=1, color=palette['ClassicGreyRed'][1], label='Score')
617
+ if sample_weight is None:
618
+ sns.distplot(y_score, bins=bins, hist=True, kde=False, ax=ax,
619
+ hist_kws={'density': True, 'rwidth': 0.95, 'color': palette['ClassicBlueRedGrey'][2], 'alpha': 1, 'label': 'Total'}, )
620
+ sns.distplot(y_score[np.where(y_true==0)], bins=bins, hist=False, kde=True,
621
+ kde_kws={'bw': 1/bins/2, 'color': palette['ClassicBlueRedGrey'][0], 'label': 'Neg KDE'}, )
622
+ sns.distplot(y_score[np.where(y_true==1)], hist=False, kde=True,
623
+ kde_kws={'bw': 1/bins/2, 'color': palette['ClassicBlueRedGrey'][1], 'label': 'Pos KDE'}, )
624
+ true_mean = np.mean(y_true)
625
+ score_mean = np.mean(y_score)
626
+ title = 'N={0:,} True={1:.2%} Score={2:.2%}'.format(
627
+ len(y_true), true_mean, score_mean
628
+ )
629
+ else:
630
+ sample_weight = np.asarray(sample_weight, dtype=float)
631
+ ax.hist(
632
+ y_score,
633
+ bins=bins,
634
+ weights=sample_weight,
635
+ density=True,
636
+ rwidth=0.95,
637
+ color=palette['ClassicBlueRedGrey'][2],
638
+ alpha=1,
639
+ label='Total (Weighted)',
640
+ )
641
+ neg_mask = y_true == 0
642
+ pos_mask = y_true == 1
643
+ _plot_weighted_kde_line(
644
+ y_score[neg_mask],
645
+ sample_weight[neg_mask],
646
+ ax,
647
+ palette['ClassicBlueRedGrey'][0],
648
+ 'Neg KDE (Weighted)',
649
+ )
650
+ _plot_weighted_kde_line(
651
+ y_score[pos_mask],
652
+ sample_weight[pos_mask],
653
+ ax,
654
+ palette['ClassicBlueRedGrey'][1],
655
+ 'Pos KDE (Weighted)',
656
+ )
657
+ true_mean = _weighted_eval.safe_weighted_average(y_true, sample_weight)
658
+ score_mean = _weighted_eval.safe_weighted_average(y_score, sample_weight)
659
+ title = 'N={0:,.2f} Raw={1:,} True={2:.2%} Score={3:.2%}'.format(
660
+ np.sum(sample_weight), len(y_true), true_mean, score_mean
661
+ )
662
+
663
+ ax.axvline(x=true_mean, linestyle='-', linewidth=1, color=palette['ClassicGreyRed'][0],label='True')
664
+ ax.axvline(x=score_mean, linestyle='--', linewidth=1, color=palette['ClassicGreyRed'][1], label='Score')
583
665
 
584
- ax.set_title('N={0:,} True={1:.2%} Score={2:.2%}'.format(len(y_true), np.mean(y_true), np.mean(y_score)), fontdict=fontdicts['subtitle'])
666
+ ax.set_title(title, fontdict=fontdicts['subtitle'])
585
667
  ax.legend(loc=1, fontsize=fontdicts['legend']['size'])
586
668
 
587
669
 
@@ -1553,6 +1635,66 @@ def __plot_multi_gain_axes(pct_dfs, ax, fontdicts):
1553
1635
  Y = np.insert(Y, 0, 0)
1554
1636
  ax.plot(X, Y, color=palette['MorandiDark'][i], linewidth=2, marker='.', markersize=5, label='{0} avgTrue'.format(md))
1555
1637
 
1638
+
1639
+ def _weighted_gains_to_plot_frames(weighted_gains):
1640
+ """Convert weighted gains output into the plotting helpers' long schema."""
1641
+ if weighted_gains is None or weighted_gains.empty:
1642
+ return pd.DataFrame(), pd.DataFrame()
1643
+
1644
+ required = {"N", "N_BAD", "AVG_SCORE", "AVG_BAD", "PROP"}
1645
+ missing = sorted(required.difference(weighted_gains.columns))
1646
+ if missing:
1647
+ raise KeyError(f"Weighted gains table missing plotting columns: {missing}")
1648
+
1649
+ descending = weighted_gains.reset_index(drop=True).copy()
1650
+ total_bad = float(descending["N_BAD"].sum())
1651
+
1652
+ def _convert(source):
1653
+ source = source.reset_index(drop=True)
1654
+ n = source["N"].to_numpy(dtype=float)
1655
+ n_bad = source["N_BAD"].to_numpy(dtype=float)
1656
+ proportion = source["PROP"].to_numpy(dtype=float)
1657
+ capture_rate = np.divide(
1658
+ n_bad,
1659
+ total_bad,
1660
+ out=np.zeros_like(n_bad, dtype=float),
1661
+ where=total_bad != 0,
1662
+ )
1663
+ cumulative_n = np.cumsum(n)
1664
+ cumulative_bad = np.cumsum(n_bad)
1665
+ return pd.DataFrame(
1666
+ {
1667
+ "thresholds": np.cumsum(proportion) * 100.0,
1668
+ "avg_score": source["AVG_SCORE"].to_numpy(dtype=float),
1669
+ "avg_true": source["AVG_BAD"].to_numpy(dtype=float),
1670
+ "proportion": proportion,
1671
+ "capture_rate": capture_rate,
1672
+ "cumsum_n": cumulative_n,
1673
+ "cumavg_true": np.divide(
1674
+ cumulative_bad,
1675
+ cumulative_n,
1676
+ out=np.full_like(cumulative_bad, np.nan, dtype=float),
1677
+ where=cumulative_n != 0,
1678
+ ),
1679
+ "gain": np.cumsum(capture_rate),
1680
+ }
1681
+ )
1682
+
1683
+ # Percentile charts historically run from low score to high score, while
1684
+ # gain charts run from the highest score downward.
1685
+ percentile_frame = _convert(descending.iloc[::-1])
1686
+ gain_frame = _convert(descending)
1687
+ return percentile_frame, gain_frame
1688
+
1689
+
1690
+ def _set_weighted_axis_title(ax, fontdicts):
1691
+ subtitle_size = fontdicts.get("subtitle", {}).get("size", 12)
1692
+ ax.set_title(
1693
+ f"Weighted\n{ax.get_title()}",
1694
+ fontsize=max(9, subtitle_size - 2),
1695
+ )
1696
+
1697
+
1556
1698
  @timeit_decorator
1557
1699
  def evaluate_performance(datasets, dist_bins=20, pct_bins=10, square_figsize=5, fontdicts=fontdicts['sub'], to_show=True, save_path=None, gains_table = True, equal_freq = True, pct_bin_edges = None, sample_weight=None):
1558
1700
  """绘制单模型预测效果评价图.
@@ -1706,15 +1848,14 @@ def __evaluate_performance(y_true, y_score, nrow, ncol, i, dist_bins, pct_bins,
1706
1848
  nbins=bin_count,
1707
1849
  weight_col="w",
1708
1850
  )
1709
- pct_df = weighted_gains
1710
- pct_desc_df = weighted_gains.iloc[::-1].reset_index(drop=True)
1851
+ pct_df, pct_desc_df = _weighted_gains_to_plot_frames(weighted_gains)
1711
1852
  if gains_table:
1712
1853
  y_gains = weighted_gains
1713
1854
  pct_info = {
1714
1855
  "pct_bins": weighted_gains.shape[0],
1715
1856
  "pct_interval": 100.0 / weighted_gains.shape[0] if weighted_gains.shape[0] else np.nan,
1716
- "pct_top_avgTrue": float(weighted_gains["AVG_BAD"].iloc[-1]) if len(weighted_gains) else np.nan,
1717
- "pct_btm_avgTrue": float(weighted_gains["AVG_BAD"].iloc[0]) if len(weighted_gains) else np.nan,
1857
+ "pct_top_avgTrue": float(weighted_gains["AVG_BAD"].iloc[0]) if len(weighted_gains) else np.nan,
1858
+ "pct_btm_avgTrue": float(weighted_gains["AVG_BAD"].iloc[-1]) if len(weighted_gains) else np.nan,
1718
1859
  }
1719
1860
  else:
1720
1861
  if pct_bin_edges is not None:
@@ -1747,16 +1888,29 @@ def __evaluate_performance(y_true, y_score, nrow, ncol, i, dist_bins, pct_bins,
1747
1888
  else:
1748
1889
  pct_desc_df = calc_equid_pct(y_true, y_score, None, bins=pct_bins, ascending=False, sample_weight=sample_weight)
1749
1890
 
1750
- __plot_single_roc_axes(roc_df, plt.subplot(nrow, ncol, i*ncol+1), fontdicts)
1751
- __plot_single_kde_axes(y_true, y_score, dist_bins, plt.subplot(nrow, ncol, i*ncol+2), fontdicts)
1891
+ ax_roc = plt.subplot(nrow, ncol, i*ncol+1)
1892
+ __plot_single_roc_axes(roc_df, ax_roc, fontdicts)
1893
+ ax_kde = plt.subplot(nrow, ncol, i*ncol+2)
1894
+ __plot_single_kde_axes(
1895
+ y_true,
1896
+ y_score,
1897
+ dist_bins,
1898
+ ax_kde,
1899
+ fontdicts,
1900
+ sample_weight=sample_weight,
1901
+ )
1752
1902
  if sample_weight is None:
1753
1903
  __plot_single_pct_axes(pct_df, plt.subplot(nrow, ncol, i*ncol+3), fontdicts)
1754
1904
  __plot_single_gain_axes(pct_desc_df, plt.subplot(nrow, ncol, i*ncol+4), fontdicts)
1755
1905
  else:
1906
+ _set_weighted_axis_title(ax_roc, fontdicts)
1907
+ _set_weighted_axis_title(ax_kde, fontdicts)
1756
1908
  ax_pct = plt.subplot(nrow, ncol, i * ncol + 3)
1757
- ax_pct.set_title("Percentile Chart (Weighted)", fontdict=fontdicts["subtitle"])
1909
+ __plot_single_pct_axes(pct_df, ax_pct, fontdicts)
1910
+ _set_weighted_axis_title(ax_pct, fontdicts)
1758
1911
  ax_gain = plt.subplot(nrow, ncol, i * ncol + 4)
1759
- ax_gain.set_title("Gain Chart (Weighted)", fontdict=fontdicts["subtitle"])
1912
+ __plot_single_gain_axes(pct_desc_df, ax_gain, fontdicts)
1913
+ _set_weighted_axis_title(ax_gain, fontdicts)
1760
1914
 
1761
1915
  info = {
1762
1916
  'N': float(np.sum(sample_weight)) if sample_weight is not None else len(y_true),