Source code for zfit.models.histogram

#  Copyright (c) 2023 zfit
from __future__ import annotations

import numpy as np
from uhi.typing.plottable import PlottableHistogram

import zfit.z.numpy as znp
from ..core.binnedpdf import BaseBinnedPDFV1
from ..core.interfaces import ZfitBinnedData
from ..core.space import supports
from ..util import ztyping
from ..util.exception import SpecificFunctionNotImplemented


[docs] class HistogramPDF(BaseBinnedPDFV1): def __init__( self, data: ztyping.BinnedDataInputType, extended: ztyping.ExtendedInputType | None = None, norm: ztyping.NormInputType | None = None, name: str = "HistogramPDF", ) -> None: """Binned PDF resembling a histogram. Simple histogram PDF that can be used to model a histogram as a PDF. Args: data: Histogram to be used as PDF. extended: |@doc:pdf.init.extended| The overall yield of the PDF. If this is parameter-like, it will be used as the yield, the expected number of events, and the PDF will be extended. An extended PDF has additional functionality, such as the ``ext_*`` methods and the ``counts`` (for binned PDFs). |@docend:pdf.init.extended| |@doc:pdf.init.extended.auto| If ``True``, the PDF will be extended automatically if the PDF is extended using the total number of events in the histogram. This is the default. |@docend:pdf.init.extended.auto| norm: |@doc:pdf.init.norm| Normalization of the PDF. By default, this is the same as the default space of the PDF. |@docend:pdf.init.norm| name: |@doc:model.init.name| Human-readable name or label of the PDF for better identification. Has no programmatical functional purpose as identification. |@docend:model.init.name| """ if extended is None: extended = True if not isinstance(data, ZfitBinnedData): if isinstance(data, PlottableHistogram): from zfit._data.binneddatav1 import BinnedData data = BinnedData.from_hist(data) else: raise TypeError( "data must be of type PlottableHistogram (UHI) or ZfitBinnedData" ) params = {} if extended is True: self._automatically_extended = True extended = znp.sum(data.values()) else: self._automatically_extended = False super().__init__( obs=data.space, extended=extended, norm=norm, params=params, name=name ) self._data = data @supports(norm="space") def _ext_pdf(self, x, norm): if not self._automatically_extended: raise SpecificFunctionNotImplemented counts = self._counts(x, norm) areas = np.prod(self._data.axes.widths, axis=0) density = counts / areas return density @supports(norm="space") def _pdf(self, x, norm): counts = self._rel_counts(x, norm) areas = np.prod(self._data.axes.widths, axis=0) density = counts / areas return density @supports(norm="space") def _counts(self, x, norm=None): if not self._automatically_extended: raise SpecificFunctionNotImplemented values = self._data.values() return values @supports(norm="space") def _rel_counts(self, x, norm=None): values = self._data.values() return values / znp.sum(values)