Source code for spey.hypothesis_testing.distributions
"""
Test statistic distributions
Similar implementation can be found in https://github.com/scikit-hep/pyhf/blob/main/src/pyhf/infer/calculators.py
"""
import numpy as np
from scipy.stats import norm
__all__ = ["AsymptoticTestStatisticsDistribution", "EmpricTestStatisticsDistribution"]
def __dir__():
return __all__
[docs]
class AsymptoticTestStatisticsDistribution:
"""
The distribution the test statistic in the asymptotic case.
Args:
shift (``float``): The shift in test statistic distribution.
"""
__slots__ = ["shift", "cutoff"]
[docs]
def __init__(self, shift: float, cutoff: float = -np.inf):
self.shift = shift
self.cutoff = cutoff
def pvalue(self, value: float) -> float:
r"""
Compute the p-value for a given value of the test statistic.
Args:
value (``float``): The test statistic value.
Returns:
``float``:
p-value
"""
return_value = norm.cdf(float(self.shift - value))
return return_value if return_value >= self.cutoff else np.nan
def logcdf(self, value: float) -> float:
r"""
Log of cumulative distribution function
Args:
value (``float``): The test statistic value.
Returns:
``float``:
log-cdf
"""
return_value = norm.logcdf(float(self.shift - value))
return return_value if return_value >= self.cutoff else np.nan
def expected_value(self, nsigma: float) -> float:
"""
Compute expected value of the test statistic with respec to the level of standard deviations
Args:
nsigma (``float``): level of standard deviations.
Returns:
``float``:
expected value of test statistic.
"""
tot = nsigma + self.shift
return tot if tot > self.cutoff else self.cutoff
[docs]
class EmpricTestStatisticsDistribution:
"""
Create emprical distribution. The p-values are computed from sampled distribution.
Args:
samples (``np.ndarray``): input samples
"""
__slots__ = ["samples"]
[docs]
def __init__(self, samples: np.ndarray) -> None:
self.samples = samples
def pvalue(self, value: float) -> float:
"""
Compute the p-value for a given test statistic.
Args:
value (``float``): test statistic value
Returns:
``float``:
p-value
"""
return np.sum(np.where(self.samples >= value, 1.0, 0.0)) / self.samples.shape[0]
def expected_value(self, nsigma: float) -> float:
"""
Compute expected value of the test statistic with respec to the level of standard deviations
Args:
nsigma (``float``): level of standard deviations.
Returns:
``float``:
expected value of test statistic.
"""
return np.percentile(self.samples, norm.cdf(nsigma) * 100.0, method="linear")