@@ -1038,15 +1038,26 @@ def _apply_figure_kwargs(fig: go.Figure, **kwargs) -> None:
10381038 fig .update_traces (** {key : val })
10391039
10401040
1041- def _forest_label (value : float , relabs : str , mode : str ) -> str :
1041+ def _forest_label (
1042+ value : float , relabs : str , mode : str , ref_value : float | None = None
1043+ ) -> str :
10421044 """Format a bound or optimal value for forest plot annotation."""
10431045 if mode == "normalized" :
10441046 return ""
10451047 if mode == "relative" and relabs == "Relative" :
10461048 return f"{ value * 100 :.4g} %"
1049+ if mode == "absolute" and relabs == "Relative" and ref_value is not None :
1050+ return f"{ value * ref_value :.4g} "
10471051 return f"{ value :.4g} "
10481052
10491053
1054+ def _forest_ref_value (p ) -> float | None :
1055+ """Return the reference value for converting a Relative parameter, or None."""
1056+ if p .relabs == "Relative" and p .init_value is not None :
1057+ return float (p .init_value [0 ])
1058+ return None
1059+
1060+
10501061def plot_parameter_forest (
10511062 parameters : list [Parameter ],
10521063 optimal_values : dict [str , float ] | list [float ] | pd .Series ,
@@ -1066,8 +1077,9 @@ def plot_parameter_forest(
10661077 How bounds are labelled depends on ``mode``:
10671078
10681079 * ``"normalized"`` — no value annotations; Y-axis ticks read 0 % … 100 %.
1069- * ``"absolute"`` — actual lower, upper, and optimal values are shown as
1070- text on each bar.
1080+ * ``"absolute"`` — for parameters with ``relabs="Absolute"``, actual bound and
1081+ optimal values are shown; for parameters with ``relabs="Relative"`` and a defined
1082+ ``init_value``, converted real values are shown (``bound * init_value``).
10711083 * ``"relative"`` — parameters with ``relabs="Relative"`` are annotated in
10721084 percent (e.g. ``interval=(0.2, 1.5)`` → ``"20 %"`` / ``"150 %"``);
10731085 parameters with ``relabs="Absolute"`` fall back to actual values.
@@ -1158,15 +1170,17 @@ def plot_parameter_forest(
11581170 # --- Labels
11591171 annotate = mode != "normalized"
11601172 lower_texts = {
1161- p .name : _forest_label (p .interval [0 ], p .relabs , mode ) for p in interval_params
1173+ p .name : _forest_label (p .interval [0 ], p .relabs , mode , _forest_ref_value (p ))
1174+ for p in interval_params
11621175 }
11631176 upper_texts = {
1164- p .name : _forest_label (p .interval [1 ], p .relabs , mode ) for p in interval_params
1177+ p .name : _forest_label (p .interval [1 ], p .relabs , mode , _forest_ref_value (p ))
1178+ for p in interval_params
11651179 }
11661180 # Optimal text: mode-aware for interval; empty for choice (tick labels already mark each position)
11671181 all_opt_texts = {
11681182 p .name : (
1169- _forest_label (float (opt_dict [p .name ]), p .relabs , mode )
1183+ _forest_label (float (opt_dict [p .name ]), p .relabs , mode , _forest_ref_value ( p ) )
11701184 if p .interval is not None
11711185 else ""
11721186 )
0 commit comments