Source code for tdub.hist

"""A module to aid working with histograms."""

from __future__ import annotations

# stdlib
import logging
from typing import Tuple, Optional, Union, Iterable

# ext
import numpy as np

log = logging.getLogger(__name__)


[docs]class SystematicComparison: """Systematic template histogram comparison. Attributes ---------- nominal : numpy.ndarray Nominal histogram bin counts. up : numpy.ndarray Up variation histogram bin counts. down : numpy.ndarray Down variation histogram bin counts. percent_diff_up : numpy.ndarray Percent difference between nominal and up varation. percent_diff_down : numpy.ndaray Percent difference between nominald and down variation. """ def __init__( self, nominal: np.ndaray, up: np.ndarray, down: np.ndarray, ) -> None: """Class constructor.""" self.nominal = nominal self.up = up self.down = down self.percent_diff_up = (up - nominal) / nominal * 100.0 self.percent_diff_down = (down - nominal) / nominal * 100.0 @property def percent_diff_min(self) -> float: """float: minimum for percent difference.""" return np.amin([self.percent_diff_up, self.percent_diff_down]) @property def percent_diff_max(self) -> float: """float: maximum for percent difference.""" return np.amax([self.percent_diff_up, self.percent_diff_down]) @property def template_max(self) -> float: """float: maximum height of a variation.""" return np.amax([self.up, self.down])
[docs] @staticmethod def one_sided(nominal: np.ndarray, up: np.ndarray) -> SystematicComparison: """Generate components of a systematic comparion plot. Parameters ---------- nominal : numpy.ndarray Histogram bin counts for the nominal template. up : numpy.ndarray Histogram bin counts for the "up" variation. Returns ------- SystematicComparison The complete description of the comparison """ diffs = nominal - up down = nominal + diffs return SystematicComparison(nominal, up, down)
[docs]def bin_centers(bin_edges: np.ndarray) -> np.ndarray: """Get bin centers given bin edges. Parameters ---------- bin_edges : numpy.ndarray edges defining binning Returns ------- numpy.ndarray the centers associated with the edges Examples -------- >>> import numpy as np >>> from tdub.hist import bin_centers >>> bin_edges = np.linspace(25, 225, 11) >>> centers = bin_centers(bin_edges) >>> bin_edges array([ 25., 45., 65., 85., 105., 125., 145., 165., 185., 205., 225.]) >>> centers array([ 35., 55., 75., 95., 115., 135., 155., 175., 195., 215.]) """ return (bin_edges[1:] + bin_edges[:-1]) * 0.5
[docs]def to_uniform_bins(bin_edges: np.ndarray): """Convert a set of variable width bins to arbitrary uniform bins. This will create a set of bin edges such that the bin centers are at whole numbers, i.e. 5 variable width bins will return an array from 0.5 to 5.5: [0.5, 1.5, 2.5, 3.5, 4.5, 5.5]. Parameters ---------- bin_edges : numpy.ndarray Array of bin edges. Returns ------- numpy.ndarray The new set of uniform bins Examples -------- >>> import numpy as np >>> from tdub.hist import to_uniform_bins >>> var_width = [0, 1, 3, 7, 15] >>> to_uniform_bins(var_width) array([0.5, 1.5, 2.5, 3.5, 4.5]) """ return np.arange(0.5, len(bin_edges) + 0.5, dtype=np.float64)
[docs]def edges_and_centers( bins: Union[int, Iterable], range: Optional[Tuple[float, float]] = None ) -> np.array: """Create arrays for edges and bin centers. Parameters ---------- bins : int or sequence of scalers the number of bins or sequence representing bin edges range : tuple(float, float), optional the minimum and maximum defining the bin range (used if bins is integral) Returns ------- :py:obj:`numpy.ndarray` the bin edges :py:obj:`numpy.ndarray` the bin centers Examples -------- from bin multiplicity and a range >>> from tdub.hist import edges_and_centers >>> edges, centers = edges_and_centers(bins=20, range=(25, 225)) from pre-existing edges >>> edges, centers = edges_and_centers(np.linspace(0, 10, 21)) """ if isinstance(bins, int): if range is None: raise ValueError("for integral bins we require the range argument") edges = np.linspace(range[0], range[1], bins + 1) else: edges = np.asarray(bins) if not np.all(edges[1:] >= edges[:-1]): raise ValueError("bins edges must monotonically increase") centers = bin_centers(edges) return edges, centers