Source code for mathematicskit.probability.systems.discrete
r"""Discrete probability distributions, built directly on :mod:`scipy.stats`.
Each class stores a frozen ``scipy.stats`` distribution for PMF/CDF/
mean/variance and adds a closed-form moment generating function (MGF),
which ``scipy.stats`` doesn't expose. See DeGroot & Schervish,
*Probability and Statistics*, 4th ed., Ch. 5, for the distributions and
their MGF derivations.
"""
from __future__ import annotations
import numpy as np
from scipy import stats
from mathematicskit.probability.core.base import DiscreteDistribution
__all__ = ["Binomial", "Poisson", "Geometric"]
[docs]
class Binomial(DiscreteDistribution):
r"""Binomial distribution: number of successes in ``n`` i.i.d. Bernoulli(``p``) trials.
:math:`P(X=k) = \binom{n}{k}p^k(1-p)^{n-k}`, via :class:`scipy.stats.binom`.
MGF: :math:`M(t) = (1-p+pe^t)^n`. See DeGroot & Schervish,
*Probability and Statistics*, 4th ed., Sec. 5.4.
Parameters
----------
n : int
Number of trials.
p : float
Success probability, ``0 <= p <= 1``.
Examples
--------
>>> b = Binomial(n=10, p=0.3)
>>> round(b.mean, 4)
3.0
>>> round(b.variance, 4)
2.1
>>> round(float(b.mgf(0.0)), 10)
1.0
"""
def __init__(self, n: int, p: float):
self.n = int(n)
self.p = float(p)
self._frozen = stats.binom(self.n, self.p)
[docs]
def pmf(self, k):
return self._frozen.pmf(k)
[docs]
def mgf(self, t):
return (1.0 - self.p + self.p * np.exp(t)) ** self.n
[docs]
class Poisson(DiscreteDistribution):
r"""Poisson distribution: count of events in a fixed interval at rate ``mu``.
:math:`P(X=k) = e^{-\mu}\mu^k/k!`, via :class:`scipy.stats.poisson`.
MGF: :math:`M(t) = e^{\mu(e^t - 1)}`. See DeGroot & Schervish,
*Probability and Statistics*, 4th ed., Sec. 5.5.
Parameters
----------
mu : float
Rate (mean number of events), ``mu > 0``.
Examples
--------
>>> p = Poisson(mu=4.0)
>>> p.mean == p.variance == 4.0
True
"""
def __init__(self, mu: float):
self.mu = float(mu)
self._frozen = stats.poisson(self.mu)
[docs]
def pmf(self, k):
return self._frozen.pmf(k)
[docs]
def mgf(self, t):
return np.exp(self.mu * (np.exp(t) - 1.0))
[docs]
class Geometric(DiscreteDistribution):
r"""Geometric distribution: number of trials up to and including the first success.
:math:`P(X=k) = (1-p)^{k-1}p`, ``k = 1, 2, \dots`` (scipy's
"number of trials" convention), via :class:`scipy.stats.geom`. MGF:
:math:`M(t) = \dfrac{pe^t}{1-(1-p)e^t}`, for :math:`t <
-\ln(1-p)`. See DeGroot & Schervish, *Probability and Statistics*,
4th ed., Sec. 5.3.
Parameters
----------
p : float
Success probability per trial, ``0 < p <= 1``.
Examples
--------
>>> g = Geometric(p=0.25)
>>> round(g.mean, 4)
4.0
"""
def __init__(self, p: float):
self.p = float(p)
self._frozen = stats.geom(self.p)
[docs]
def pmf(self, k):
return self._frozen.pmf(k)
[docs]
def mgf(self, t):
return self.p * np.exp(t) / (1.0 - (1.0 - self.p) * np.exp(t))