Source code for configurable_spectrograms.percentile_utils
"""Axis-extrema rounding and percentile-bound computation for color scales."""
import math
import numpy as np
[docs]
def round_extrema(value: float | int, direction: str) -> float:
"""Round an extrema value to a clean significant-digit axis limit.
Rounds to the next significant digit in the specified direction so plot
axis limits look consistent (e.g. 1234 -> 1300 for 'up').
Parameters
----------
value : float or int
Extrema value. Zero returns 0.0.
direction : {'up', 'down'}
Round up (for maxima) or down (for minima).
Returns
-------
float
Raises
------
ValueError
If direction is not ``'up'`` or ``'down'``.
Examples
--------
>>> round_extrema(1234, 'up')
1300.0
>>> round_extrema(0.0123, 'down')
0.012
"""
if value == 0:
return 0.0
factor = 10 ** (math.floor(math.log10(abs(value))) - 1)
if direction == "up":
return float(math.ceil(value / factor) * factor)
if direction == "down":
return float(math.floor(value / factor) * factor)
raise ValueError(f"Invalid direction: {direction}")
[docs]
def compute_percentile_bounds(
matrix: np.ndarray,
low_percentile: float = 1,
high_percentile: float = 99,
z_min: float | None = None,
z_max: float | None = None,
) -> tuple[float, float]:
"""Return ``(z_min, z_max)`` color-scale bounds for a data matrix.
Explicit ``z_min``/``z_max`` values are used as-is when given; otherwise
each bound is computed independently via ``numpy.nanpercentile``. This
unifies the vmin/vmax percentile logic that plotting functions need when
the caller hasn't supplied fixed bounds.
Parameters
----------
matrix : numpy.ndarray
Data array (NaNs ignored).
low_percentile : float, default 1
Percentile used for the lower bound when ``z_min`` is ``None``.
high_percentile : float, default 99
Percentile used for the upper bound when ``z_max`` is ``None``.
z_min : float or None, optional
Explicit lower bound; overrides ``low_percentile`` when given.
z_max : float or None, optional
Explicit upper bound; overrides ``high_percentile`` when given.
Returns
-------
tuple of float
``(z_min, z_max)``.
Examples
--------
>>> import numpy as np
>>> compute_percentile_bounds(np.array([[1.0, 2.0, 3.0, 100.0]]), 0, 100)
(1.0, 100.0)
>>> compute_percentile_bounds(np.array([1.0, 2.0, 3.0]), z_min=-5.0, z_max=5.0)
(-5.0, 5.0)
"""
resolved_min = float(z_min) if z_min is not None else float(np.nanpercentile(matrix, low_percentile))
resolved_max = float(z_max) if z_max is not None else float(np.nanpercentile(matrix, high_percentile))
return resolved_min, resolved_max