|
57 | 57 | 'recall_macro': 'Recall (Macro)' |
58 | 58 | } |
59 | 59 |
|
| 60 | +# Metrics where lower values are better |
| 61 | +_LOWER_IS_BETTER_METRICS = {'mae', 'mape', 'rmse', 'mse'} |
| 62 | + |
| 63 | + |
| 64 | +def _is_lower_better(metric: str) -> bool: |
| 65 | + """Return True if lower values are better for this metric.""" |
| 66 | + return metric.lower() in _LOWER_IS_BETTER_METRICS |
| 67 | + |
| 68 | + |
| 69 | +def _add_good_direction_annotation(ax: plt.Axes, metric: str, compact: bool = False) -> None: |
| 70 | + """Add a small annotation near the y-axis indicating the direction of improvement. |
| 71 | +
|
| 72 | + Plots '↓ better' for error metrics (MAE, MAPE) where lower is better, |
| 73 | + and '↑ better' for score metrics (F1, Accuracy, R²) where higher is better. |
| 74 | + """ |
| 75 | + arrow = '↓' if _is_lower_better(metric) else '↑' |
| 76 | + fontsize = 6 if compact else 7 |
| 77 | + ax.text( |
| 78 | + 0, 1.02, f'{arrow} better', |
| 79 | + transform=ax.transAxes, |
| 80 | + fontsize=fontsize, |
| 81 | + ha='left', va='bottom', |
| 82 | + color='gray', fontstyle='italic', |
| 83 | + ) |
| 84 | + |
60 | 85 |
|
61 | 86 | def plot_performance_comparison(results: Dict[str, Any], |
62 | 87 | target_type: str = 'regression', |
@@ -133,6 +158,9 @@ def plot_performance_comparison(results: Dict[str, Any], |
133 | 158 | ax.set_ylabel(metric_label, fontsize=10, labelpad=15) |
134 | 159 | ax.yaxis.set_label_coords(-0.45, 0.5) # Fixed position with better padding |
135 | 160 |
|
| 161 | + # Indicate which direction is better |
| 162 | + _add_good_direction_annotation(ax, metric) |
| 163 | + |
136 | 164 | # Apply range frame |
137 | 165 | if len(valid_means) > 0: |
138 | 166 | range_frame(ax, x_positions, valid_means) |
@@ -445,6 +473,9 @@ def plot_parameter_sweep_results(sweep_results: List[Dict[str, Any]], |
445 | 473 | ax.legend(fontsize=8) |
446 | 474 | ax.set_xscale('log') |
447 | 475 |
|
| 476 | + # Indicate which direction is better |
| 477 | + _add_good_direction_annotation(ax, metric) |
| 478 | + |
448 | 479 | # Apply range frame |
449 | 480 | if all_x and all_y: |
450 | 481 | range_frame(ax, np.array(all_x), np.array(all_y)) |
@@ -594,6 +625,9 @@ def _plot_meta_prediction_panel(ax, results: Dict[str, Any], meta_type: str, |
594 | 625 | else: |
595 | 626 | ax.set_ylabel(ylabel_text, fontsize=fontsize_tick+1, labelpad=10) |
596 | 627 |
|
| 628 | + # Indicate which direction is better |
| 629 | + _add_good_direction_annotation(ax, metric, compact=compact) |
| 630 | + |
597 | 631 | ax.set_xticks(x_pos) |
598 | 632 | ax.set_xticklabels(labels_list, rotation=30, ha='right', fontsize=fontsize_tick) |
599 | 633 |
|
@@ -746,6 +780,9 @@ def create_main_figure_panel(results: Dict[str, Any], |
746 | 780 | # Use manual ylabel for better control |
747 | 781 | ax_perf.set_ylabel(metric_label, fontsize=10, labelpad=12) |
748 | 782 |
|
| 783 | + # Indicate which direction is better |
| 784 | + _add_good_direction_annotation(ax_perf, main_metric) |
| 785 | + |
749 | 786 | # Add panel label D |
750 | 787 | ax_perf.text(-0.03, 1.05, 'D', transform=ax_perf.transAxes, |
751 | 788 | fontsize=12, fontweight='bold', ha='center') |
@@ -974,6 +1011,9 @@ def plot_meta_comparison(comparison_results: Dict[str, Any], |
974 | 1011 | metric_label = metric_labels.get(primary_metric, primary_metric.upper()) if metric_labels else primary_metric.upper() |
975 | 1012 | ax_property.set_ylabel(metric_label) |
976 | 1013 |
|
| 1014 | + # Indicate which direction is better |
| 1015 | + _add_good_direction_annotation(ax_property, primary_metric) |
| 1016 | + |
977 | 1017 | # Add baseline reference if applicable |
978 | 1018 | if not np.isnan(means[1]): # dummy baseline |
979 | 1019 | range_frame(ax_property, np.array([0]), np.array([means[1]])) |
|
0 commit comments