@@ -199,6 +199,67 @@ def _apply_secondary_ticks(ax, axis="x"):
199199 axis_obj .set_minor_locator (FixedLocator (sorted (minor_ticks )))
200200
201201
202+ def _determine_ratio_window (ratio_arrays , data_ratio_arrays , * , tolerance = 1e-12 ):
203+ """Return ratio axis limits and warning flags given MC/data ratio samples."""
204+
205+ ratio_windows = (
206+ (0.5 , 1.5 ),
207+ (0.0 , 2.0 ),
208+ (- 1.0 , 3.0 ),
209+ )
210+ ratio_window_deviations = (0.5 , 1.0 , 2.0 )
211+ largest_low , largest_high = ratio_windows [- 1 ]
212+
213+ def _finite_segments (arrays ):
214+ segments = []
215+ for arr in arrays or ():
216+ if arr is None :
217+ continue
218+ arr = np .asarray (arr , dtype = float )
219+ finite_mask = np .isfinite (arr )
220+ if np .any (finite_mask ):
221+ segments .append (arr [finite_mask ])
222+ return segments
223+
224+ finite_segments = _finite_segments (ratio_arrays )
225+
226+ ratio_limits = ratio_windows [0 ]
227+ exceeds_largest_window = False
228+ if finite_segments :
229+ combined = np .concatenate (finite_segments )
230+ min_val = float (np .min (combined ))
231+ max_val = float (np .max (combined ))
232+ max_abs_deviation = float (np .max (np .abs (combined - 1.0 )))
233+
234+ selected_limits = ratio_windows [- 1 ]
235+ for (low , high ), allowed_deviation in zip (ratio_windows , ratio_window_deviations ):
236+ if (
237+ max_abs_deviation <= allowed_deviation + tolerance
238+ and min_val >= low - tolerance
239+ and max_val <= high + tolerance
240+ ):
241+ selected_limits = (low , high )
242+ break
243+
244+ ratio_limits = selected_limits
245+
246+ exceeds_largest_window = (
247+ min_val < largest_low - tolerance or max_val > largest_high + tolerance
248+ )
249+
250+ data_finite_segments = _finite_segments (data_ratio_arrays )
251+ data_exceeds_largest_window = False
252+ if data_finite_segments :
253+ data_combined = np .concatenate (data_finite_segments )
254+ data_min = float (np .min (data_combined ))
255+ data_max = float (np .max (data_combined ))
256+ data_exceeds_largest_window = (
257+ data_min < largest_low - tolerance or data_max > largest_high + tolerance
258+ )
259+
260+ return ratio_limits , exceeds_largest_window , data_exceeds_largest_window
261+
262+
202263def _merge_mappings (base , updates ):
203264 if not isinstance (base , dict ) or not isinstance (updates , Mapping ):
204265 return base
@@ -3406,62 +3467,11 @@ def make_region_stacked_ratio_fig(
34063467 if arr is not None :
34073468 ratio_arrays .append (np .asarray (arr , dtype = float ))
34083469
3409- ratio_windows = (
3410- (0.5 , 1.5 ),
3411- (0.0 , 2.0 ),
3412- (- 1.0 , 3.0 ),
3413- )
3414- ratio_window_deviations = (0.5 , 1.0 , 2.0 )
3415- largest_low , largest_high = ratio_windows [- 1 ]
3416- finite_segments = []
3417- for arr in ratio_arrays :
3418- if arr is None :
3419- continue
3420- finite_mask = np .isfinite (arr )
3421- if np .any (finite_mask ):
3422- finite_segments .append (arr [finite_mask ])
3423-
3424- ratio_limits = ratio_windows [0 ]
3425- tolerance = 1e-12
3426- exceeds_largest_window = False
3427- if finite_segments :
3428- combined = np .concatenate (finite_segments )
3429- min_val = float (np .min (combined ))
3430- max_val = float (np .max (combined ))
3431- max_abs_deviation = float (np .max (np .abs (combined - 1.0 )))
3432-
3433- selected_limits = ratio_windows [- 1 ]
3434- for (low , high ), allowed_deviation in zip (ratio_windows , ratio_window_deviations ):
3435- if (
3436- max_abs_deviation <= allowed_deviation + tolerance
3437- and min_val >= low - tolerance
3438- and max_val <= high + tolerance
3439- ):
3440- selected_limits = (low , high )
3441- break
3442-
3443- ratio_limits = selected_limits
3444-
3445- exceeds_largest_window = (
3446- min_val < largest_low - tolerance or max_val > largest_high + tolerance
3447- )
3448-
3449- data_finite_segments = []
3450- for arr in data_ratio_arrays :
3451- if arr is None :
3452- continue
3453- finite_mask = np .isfinite (arr )
3454- if np .any (finite_mask ):
3455- data_finite_segments .append (arr [finite_mask ])
3456-
3457- data_exceeds_largest_window = False
3458- if data_finite_segments :
3459- data_combined = np .concatenate (data_finite_segments )
3460- data_min = float (np .min (data_combined ))
3461- data_max = float (np .max (data_combined ))
3462- data_exceeds_largest_window = (
3463- data_min < largest_low - tolerance or data_max > largest_high + tolerance
3464- )
3470+ (
3471+ ratio_limits ,
3472+ exceeds_largest_window ,
3473+ data_exceeds_largest_window ,
3474+ ) = _determine_ratio_window (ratio_arrays , data_ratio_arrays )
34653475
34663476 if exceeds_largest_window or data_exceeds_largest_window :
34673477 warnings .warn (
0 commit comments