Source code for pyhs3.distributions.histfactory.samples
"""
HistFactory Distribution implementation.
Provides the HistFactoryDist class for handling binned statistical models
with samples and modifiers as defined in the HS3 specification.
"""
from __future__ import annotations
import hist
import numpy as np
from pydantic import Field
from pyhs3.axes import BinnedAxes
from pyhs3.collections import NamedCollection, NamedModel
# Import existing distributions for constraint terms
from pyhs3.distributions.histfactory.data import SampleData
from pyhs3.distributions.histfactory.modifiers import Modifiers
[docs]
class Sample(NamedModel):
"""HistFactory sample specification."""
data: SampleData
modifiers: Modifiers = Field(default_factory=Modifiers)
def to_hist(self, axes: BinnedAxes) -> hist.Hist[hist.storage.Weight]:
"""
Convert to scikit-hep hist.Hist object for visualization.
Creates a hist.Hist histogram from this sample's contents and errors.
The axes must be provided since SampleData doesn't contain axis information.
Args:
axes: BinnedAxes specification defining the binning
Returns:
hist.Hist: Histogram representation with:
- Axes matching the provided axes
- Values from sample contents
- Variances from sample errors (squared)
Examples:
>>> from pyhs3.axes import BinnedAxes
>>> sample = Sample(
... name="signal",
... data={"contents": [10, 20, 15], "errors": [3, 4, 2.5]}
... )
>>> axes = BinnedAxes([{"name": "x", "min": 0, "max": 3, "nbins": 3}])
>>> sample.to_hist(axes)
Hist(Regular(3, 0, 3, name='x'), storage=Weight()) # Sum: WeightedSum(value=45, variance=31.25)
"""
# Convert axes to hist.axis objects
# Access the root to get the actual axis (RegularAxis or IrregularAxis)
hist_axes = [axis.to_hist() for axis in axes]
# Create histogram with Weight storage since we always have errors
h = hist.Hist(*hist_axes, storage=hist.storage.Weight())
# Calculate shape from axes
shape = tuple(axis.nbins for axis in axes)
# Reshape contents and variances (errors squared)
contents_nd = np.array(self.data.contents).reshape(shape)
variances_nd = np.square(self.data.errors).reshape(shape)
stacked = np.stack([contents_nd, variances_nd], axis=-1)
h[...] = stacked
return h
[docs]
class Samples(NamedCollection[Sample]):
"""
Collection of samples for a HistFactory distribution.
Manages a set of sample instances, providing dict-like access by sample name
and list-like iteration. Handles sample validation and maintains name uniqueness.
"""
root: list[Sample] = Field(default_factory=list)
__all__ = ("Sample", "Samples")