Source code for mathematicskit.statistics.systems.confidence_intervals

r"""Confidence intervals for means, proportions, and variances.

Reference distributions: :class:`mathematicskit.probability.Normal` for the
(known-variance) mean and proportion intervals, :class:`scipy.stats.t`
for the (unknown-variance) mean interval, and :class:`scipy.stats.chi2`
for the variance interval -- the quantile-function calls themselves are
the whole interval construction, no reason to hand-roll them. See
DeGroot & Schervish, *Probability and Statistics*, 4th ed., Ch. 9.
"""

from __future__ import annotations

from typing import Optional

import numpy as np
from scipy import stats

from mathematicskit.probability import Normal
from mathematicskit.statistics.core.base import ConfidenceIntervalResult

__all__ = ["mean_confidence_interval", "proportion_confidence_interval", "variance_confidence_interval"]

_STANDARD_NORMAL = Normal(mu=0.0, sigma=1.0)


[docs] def mean_confidence_interval(data: np.ndarray, sigma: Optional[float] = None, confidence_level: float = 0.95) -> ConfidenceIntervalResult: r"""Confidence interval for a population mean. :math:`\bar x \pm z_{\alpha/2}\,\sigma/\sqrt n` if `sigma` (the population standard deviation) is known, else :math:`\bar x \pm t_{\alpha/2,\,n-1}\,s/\sqrt n` using the sample standard deviation. See DeGroot & Schervish, *Probability and Statistics*, 4th ed., Sec. 8.5-8.6. Parameters ---------- data : array-like, shape (n,) sigma : float, optional Known population standard deviation; if omitted, the sample standard deviation and Student's t distribution are used instead. confidence_level : float Returns ------- ConfidenceIntervalResult Examples -------- >>> import numpy as np >>> rng = np.random.default_rng(0) >>> data = rng.normal(loc=10.0, scale=2.0, size=100) >>> result = mean_confidence_interval(data, sigma=2.0) >>> bool(result.lower < 10.0 < result.upper) True """ x = np.asarray(data, dtype=np.float64) n = x.shape[0] mean = float(np.mean(x)) alpha = 1.0 - confidence_level if sigma is not None: z = _STANDARD_NORMAL.ppf(1.0 - alpha / 2.0) margin = z * sigma / np.sqrt(n) method = "z" else: df = n - 1 t_crit = stats.t.ppf(1.0 - alpha / 2.0, df) margin = t_crit * float(np.std(x, ddof=1)) / np.sqrt(n) method = "t" return ConfidenceIntervalResult(estimate=mean, lower=mean - margin, upper=mean + margin, confidence_level=confidence_level, method=method)
[docs] def proportion_confidence_interval(successes: int, n: int, confidence_level: float = 0.95) -> ConfidenceIntervalResult: r"""Wald (normal-approximation) confidence interval for a population proportion. :math:`\hat p \pm z_{\alpha/2}\sqrt{\hat p(1-\hat p)/n}`, valid for ``n`` large enough that the normal approximation to the binomial holds (a common rule of thumb: :math:`n\hat p \geq 5` and :math:`n(1-\hat p) \geq 5`). See DeGroot & Schervish, *Probability and Statistics*, 4th ed., Sec. 9.5. Parameters ---------- successes : int n : int confidence_level : float Returns ------- ConfidenceIntervalResult Examples -------- >>> result = proportion_confidence_interval(successes=520, n=1000) >>> round(result.estimate, 4) 0.52 """ p_hat = successes / n alpha = 1.0 - confidence_level z = _STANDARD_NORMAL.ppf(1.0 - alpha / 2.0) margin = z * np.sqrt(p_hat * (1.0 - p_hat) / n) return ConfidenceIntervalResult( estimate=p_hat, lower=max(0.0, p_hat - margin), upper=min(1.0, p_hat + margin), confidence_level=confidence_level, method="wald_proportion" )
[docs] def variance_confidence_interval(data: np.ndarray, confidence_level: float = 0.95) -> ConfidenceIntervalResult: r"""Confidence interval for a population variance, assuming normality. :math:`\left(\dfrac{(n-1)s^2}{\chi^2_{\alpha/2,\,n-1}}, \dfrac{(n-1)s^2}{\chi^2_{1-\alpha/2,\,n-1}}\right)`, via :class:`scipy.stats.chi2`. See DeGroot & Schervish, *Probability and Statistics*, 4th ed., Sec. 9.8. Parameters ---------- data : array-like, shape (n,) confidence_level : float Returns ------- ConfidenceIntervalResult Examples -------- >>> import numpy as np >>> rng = np.random.default_rng(0) >>> data = rng.normal(loc=0.0, scale=3.0, size=200) >>> result = variance_confidence_interval(data) >>> bool(result.lower < 9.0 < result.upper) True """ x = np.asarray(data, dtype=np.float64) n = x.shape[0] df = n - 1 s2 = float(np.var(x, ddof=1)) alpha = 1.0 - confidence_level chi2_upper = stats.chi2.ppf(1.0 - alpha / 2.0, df) chi2_lower = stats.chi2.ppf(alpha / 2.0, df) return ConfidenceIntervalResult( estimate=s2, lower=df * s2 / chi2_upper, upper=df * s2 / chi2_lower, confidence_level=confidence_level, method="chi_square_variance" )