Source code for gammapy.stats.normal

# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""Conversion functions for test statistic <-> significance <-> probability."""
import numpy as np
from scipy.stats import norm
from scipy.special import erfinv, erf
from scipy.optimize import fsolve


__all__ = [
    "significance_to_probability_normal",
    "probability_to_significance_normal",
    "probability_to_significance_normal_limit",
    "significance_to_probability_normal_limit",
]


[docs]def significance_to_probability_normal(significance): """Convert significance to one-sided tail probability. Parameters ---------- significance : array_like Significance Returns ------- probability : `numpy.ndarray` One-sided tail probability See Also -------- probability_to_significance_normal, significance_to_probability_normal_limit Examples -------- >>> significance_to_probability_normal(0) 0.5 >>> significance_to_probability_normal(1) 0.15865525393145707 >>> significance_to_probability_normal(3) 0.0013498980316300933 >>> significance_to_probability_normal(5) 2.8665157187919328e-07 >>> significance_to_probability_normal(10) 7.6198530241604696e-24 """ return norm.sf(significance)
[docs]def probability_to_significance_normal(probability): """Convert one-sided tail probability to significance. Parameters ---------- probability : array_like One-sided tail probability Returns ------- significance : ndarray Significance See Also -------- significance_to_probability_normal, probability_to_significance_normal_limit Examples -------- >>> probability_to_significance_normal(1e-10) 6.3613409024040557 """ return norm.isf(probability)
def _p_to_s_direct(probability, one_sided=True): """Direct implementation of p_to_s for checking. Reference: RooStats User Guide Equations (6,7). """ probability = 1 - probability # We want p to be the tail probability temp = np.where(one_sided, 2 * probability - 1, probability) return np.sqrt(2) * erfinv(temp) def _s_to_p_direct(significance, one_sided=True): """Direct implementation of s_to_p for checking. Note: _p_to_s_direct was solved for p. """ temp = erf(significance / np.sqrt(2)) probability = np.where(one_sided, (temp + 1) / 2.0, temp) return 1 - probability # We want p to be the tail probability
[docs]def probability_to_significance_normal_limit(probability): r"""Tail probability to significance in small probability limit. Reference: https://ui.adsabs.harvard.edu/abs/2007physics...2156C, Equation (4) They say it is better than 1% for s > 1.6. Asymptotically: :math:`s \sim \sqrt{-\log(p)}` See also: `significance_to_probability_normal_limit` """ u = -2 * np.log(probability * np.sqrt(2 * np.pi)) return np.sqrt(u - np.log(u))
[docs]def significance_to_probability_normal_limit(significance, guess=1e-100): """Significance to tail probability in large significance limit. See also: `probability_to_significance_normal_limit` """ # Note: s^2 = u - log(u) can't be solved analytically. def f(probability): if probability > 0: return probability_to_significance_normal_limit(probability) - significance else: return 1e100 return fsolve(f, guess)