/scipy/stats/_continuous_distns.py
Python | 1846 lines | 1802 code | 7 blank | 37 comment | 7 complexity | 790fa5e2faa5ee1f6aa083fd4f08f4ae MD5 | raw file
- # -*- coding: utf-8 -*-
- #
- # Author: Travis Oliphant 2002-2011 with contributions from
- # SciPy Developers 2004-2011
- #
- import warnings
- from collections.abc import Iterable
- from functools import wraps
- import ctypes
- import numpy as np
- from scipy._lib.doccer import (extend_notes_in_docstring,
- replace_notes_in_docstring)
- from scipy._lib._ccallback import LowLevelCallable
- from scipy import optimize
- from scipy import integrate
- from scipy import interpolate
- import scipy.special as sc
- import scipy.special._ufuncs as scu
- from scipy._lib._util import _lazyselect, _lazywhere
- from . import _stats
- from ._rvs_sampling import rvs_ratio_uniforms
- from ._tukeylambda_stats import (tukeylambda_variance as _tlvar,
- tukeylambda_kurtosis as _tlkurt)
- from ._distn_infrastructure import (
- get_distribution_names, _kurtosis, _ncx2_cdf, _ncx2_log_pdf, _ncx2_pdf,
- rv_continuous, _skew, _get_fixed_fit_value, _check_shape)
- from ._ksstats import kolmogn, kolmognp, kolmogni
- from ._constants import (_XMIN, _EULER, _ZETA3,
- _SQRT_2_OVER_PI, _LOG_SQRT_2_OVER_PI)
- import scipy.stats._boost as _boost
- def _remove_optimizer_parameters(kwds):
- """
- Remove the optimizer-related keyword arguments 'loc', 'scale' and
- 'optimizer' from `kwds`. Then check that `kwds` is empty, and
- raise `TypeError("Unknown arguments: %s." % kwds)` if it is not.
- This function is used in the fit method of distributions that override
- the default method and do not use the default optimization code.
- `kwds` is modified in-place.
- """
- kwds.pop('loc', None)
- kwds.pop('scale', None)
- kwds.pop('optimizer', None)
- kwds.pop('method', None)
- if kwds:
- raise TypeError("Unknown arguments: %s." % kwds)
- def _call_super_mom(fun):
- # if fit method is overridden only for MLE and doesn't specify what to do
- # if method == 'mm', this decorator calls generic implementation
- @wraps(fun)
- def wrapper(self, *args, **kwds):
- method = kwds.get('method', 'mle').lower()
- if method == 'mm':
- return super(type(self), self).fit(*args, **kwds)
- else:
- return fun(self, *args, **kwds)
- return wrapper
- class ksone_gen(rv_continuous):
- r"""Kolmogorov-Smirnov one-sided test statistic distribution.
- This is the distribution of the one-sided Kolmogorov-Smirnov (KS)
- statistics :math:`D_n^+` and :math:`D_n^-`
- for a finite sample size ``n`` (the shape parameter).
- %(before_notes)s
- See Also
- --------
- kstwobign, kstwo, kstest
- Notes
- -----
- :math:`D_n^+` and :math:`D_n^-` are given by
- .. math::
- D_n^+ &= \text{sup}_x (F_n(x) - F(x)),\\
- D_n^- &= \text{sup}_x (F(x) - F_n(x)),\\
- where :math:`F` is a continuous CDF and :math:`F_n` is an empirical CDF.
- `ksone` describes the distribution under the null hypothesis of the KS test
- that the empirical CDF corresponds to :math:`n` i.i.d. random variates
- with CDF :math:`F`.
- %(after_notes)s
- References
- ----------
- .. [1] Birnbaum, Z. W. and Tingey, F.H. "One-sided confidence contours
- for probability distribution functions", The Annals of Mathematical
- Statistics, 22(4), pp 592-596 (1951).
- %(example)s
- """
- def _pdf(self, x, n):
- return -scu._smirnovp(n, x)
- def _cdf(self, x, n):
- return scu._smirnovc(n, x)
- def _sf(self, x, n):
- return sc.smirnov(n, x)
- def _ppf(self, q, n):
- return scu._smirnovci(n, q)
- def _isf(self, q, n):
- return sc.smirnovi(n, q)
- ksone = ksone_gen(a=0.0, b=1.0, name='ksone')
- class kstwo_gen(rv_continuous):
- r"""Kolmogorov-Smirnov two-sided test statistic distribution.
- This is the distribution of the two-sided Kolmogorov-Smirnov (KS)
- statistic :math:`D_n` for a finite sample size ``n``
- (the shape parameter).
- %(before_notes)s
- See Also
- --------
- kstwobign, ksone, kstest
- Notes
- -----
- :math:`D_n` is given by
- .. math::
- D_n = \text{sup}_x |F_n(x) - F(x)|
- where :math:`F` is a (continuous) CDF and :math:`F_n` is an empirical CDF.
- `kstwo` describes the distribution under the null hypothesis of the KS test
- that the empirical CDF corresponds to :math:`n` i.i.d. random variates
- with CDF :math:`F`.
- %(after_notes)s
- References
- ----------
- .. [1] Simard, R., L'Ecuyer, P. "Computing the Two-Sided
- Kolmogorov-Smirnov Distribution", Journal of Statistical Software,
- Vol 39, 11, 1-18 (2011).
- %(example)s
- """
- def _get_support(self, n):
- return (0.5/(n if not isinstance(n, Iterable) else np.asanyarray(n)),
- 1.0)
- def _pdf(self, x, n):
- return kolmognp(n, x)
- def _cdf(self, x, n):
- return kolmogn(n, x)
- def _sf(self, x, n):
- return kolmogn(n, x, cdf=False)
- def _ppf(self, q, n):
- return kolmogni(n, q, cdf=True)
- def _isf(self, q, n):
- return kolmogni(n, q, cdf=False)
- # Use the pdf, (not the ppf) to compute moments
- kstwo = kstwo_gen(momtype=0, a=0.0, b=1.0, name='kstwo')
- class kstwobign_gen(rv_continuous):
- r"""Limiting distribution of scaled Kolmogorov-Smirnov two-sided test statistic.
- This is the asymptotic distribution of the two-sided Kolmogorov-Smirnov
- statistic :math:`\sqrt{n} D_n` that measures the maximum absolute
- distance of the theoretical (continuous) CDF from the empirical CDF.
- (see `kstest`).
- %(before_notes)s
- See Also
- --------
- ksone, kstwo, kstest
- Notes
- -----
- :math:`\sqrt{n} D_n` is given by
- .. math::
- D_n = \text{sup}_x |F_n(x) - F(x)|
- where :math:`F` is a continuous CDF and :math:`F_n` is an empirical CDF.
- `kstwobign` describes the asymptotic distribution (i.e. the limit of
- :math:`\sqrt{n} D_n`) under the null hypothesis of the KS test that the
- empirical CDF corresponds to i.i.d. random variates with CDF :math:`F`.
- %(after_notes)s
- References
- ----------
- .. [1] Feller, W. "On the Kolmogorov-Smirnov Limit Theorems for Empirical
- Distributions", Ann. Math. Statist. Vol 19, 177-189 (1948).
- %(example)s
- """
- def _pdf(self, x):
- return -scu._kolmogp(x)
- def _cdf(self, x):
- return scu._kolmogc(x)
- def _sf(self, x):
- return sc.kolmogorov(x)
- def _ppf(self, q):
- return scu._kolmogci(q)
- def _isf(self, q):
- return sc.kolmogi(q)
- kstwobign = kstwobign_gen(a=0.0, name='kstwobign')
- ## Normal distribution
- # loc = mu, scale = std
- # Keep these implementations out of the class definition so they can be reused
- # by other distributions.
- _norm_pdf_C = np.sqrt(2*np.pi)
- _norm_pdf_logC = np.log(_norm_pdf_C)
- def _norm_pdf(x):
- return np.exp(-x**2/2.0) / _norm_pdf_C
- def _norm_logpdf(x):
- return -x**2 / 2.0 - _norm_pdf_logC
- def _norm_cdf(x):
- return sc.ndtr(x)
- def _norm_logcdf(x):
- return sc.log_ndtr(x)
- def _norm_ppf(q):
- return sc.ndtri(q)
- def _norm_sf(x):
- return _norm_cdf(-x)
- def _norm_logsf(x):
- return _norm_logcdf(-x)
- def _norm_isf(q):
- return -_norm_ppf(q)
- class norm_gen(rv_continuous):
- r"""A normal continuous random variable.
- The location (``loc``) keyword specifies the mean.
- The scale (``scale``) keyword specifies the standard deviation.
- %(before_notes)s
- Notes
- -----
- The probability density function for `norm` is:
- .. math::
- f(x) = \frac{\exp(-x^2/2)}{\sqrt{2\pi}}
- for a real number :math:`x`.
- %(after_notes)s
- %(example)s
- """
- def _rvs(self, size=None, random_state=None):
- return random_state.standard_normal(size)
- def _pdf(self, x):
- # norm.pdf(x) = exp(-x**2/2)/sqrt(2*pi)
- return _norm_pdf(x)
- def _logpdf(self, x):
- return _norm_logpdf(x)
- def _cdf(self, x):
- return _norm_cdf(x)
- def _logcdf(self, x):
- return _norm_logcdf(x)
- def _sf(self, x):
- return _norm_sf(x)
- def _logsf(self, x):
- return _norm_logsf(x)
- def _ppf(self, q):
- return _norm_ppf(q)
- def _isf(self, q):
- return _norm_isf(q)
- def _stats(self):
- return 0.0, 1.0, 0.0, 0.0
- def _entropy(self):
- return 0.5*(np.log(2*np.pi)+1)
- @_call_super_mom
- @replace_notes_in_docstring(rv_continuous, notes="""\
- For the normal distribution, method of moments and maximum likelihood
- estimation give identical fits, and explicit formulas for the estimates
- are available.
- This function uses these explicit formulas for the maximum likelihood
- estimation of the normal distribution parameters, so the
- `optimizer` and `method` arguments are ignored.\n\n""")
- def fit(self, data, **kwds):
- floc = kwds.pop('floc', None)
- fscale = kwds.pop('fscale', None)
- _remove_optimizer_parameters(kwds)
- if floc is not None and fscale is not None:
- # This check is for consistency with `rv_continuous.fit`.
- # Without this check, this function would just return the
- # parameters that were given.
- raise ValueError("All parameters fixed. There is nothing to "
- "optimize.")
- data = np.asarray(data)
- if not np.isfinite(data).all():
- raise RuntimeError("The data contains non-finite values.")
- if floc is None:
- loc = data.mean()
- else:
- loc = floc
- if fscale is None:
- scale = np.sqrt(((data - loc)**2).mean())
- else:
- scale = fscale
- return loc, scale
- def _munp(self, n):
- """
- @returns Moments of standard normal distribution for integer n >= 0
- See eq. 16 of https://arxiv.org/abs/1209.4340v2
- """
- if n % 2 == 0:
- return sc.factorial2(n - 1)
- else:
- return 0.
- norm = norm_gen(name='norm')
- class alpha_gen(rv_continuous):
- r"""An alpha continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `alpha` ([1]_, [2]_) is:
- .. math::
- f(x, a) = \frac{1}{x^2 \Phi(a) \sqrt{2\pi}} *
- \exp(-\frac{1}{2} (a-1/x)^2)
- where :math:`\Phi` is the normal CDF, :math:`x > 0`, and :math:`a > 0`.
- `alpha` takes ``a`` as a shape parameter.
- %(after_notes)s
- References
- ----------
- .. [1] Johnson, Kotz, and Balakrishnan, "Continuous Univariate
- Distributions, Volume 1", Second Edition, John Wiley and Sons,
- p. 173 (1994).
- .. [2] Anthony A. Salvia, "Reliability applications of the Alpha
- Distribution", IEEE Transactions on Reliability, Vol. R-34,
- No. 3, pp. 251-252 (1985).
- %(example)s
- """
- _support_mask = rv_continuous._open_support_mask
- def _pdf(self, x, a):
- # alpha.pdf(x, a) = 1/(x**2*Phi(a)*sqrt(2*pi)) * exp(-1/2 * (a-1/x)**2)
- return 1.0/(x**2)/_norm_cdf(a)*_norm_pdf(a-1.0/x)
- def _logpdf(self, x, a):
- return -2*np.log(x) + _norm_logpdf(a-1.0/x) - np.log(_norm_cdf(a))
- def _cdf(self, x, a):
- return _norm_cdf(a-1.0/x) / _norm_cdf(a)
- def _ppf(self, q, a):
- return 1.0/np.asarray(a-sc.ndtri(q*_norm_cdf(a)))
- def _stats(self, a):
- return [np.inf]*2 + [np.nan]*2
- alpha = alpha_gen(a=0.0, name='alpha')
- class anglit_gen(rv_continuous):
- r"""An anglit continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `anglit` is:
- .. math::
- f(x) = \sin(2x + \pi/2) = \cos(2x)
- for :math:`-\pi/4 \le x \le \pi/4`.
- %(after_notes)s
- %(example)s
- """
- def _pdf(self, x):
- # anglit.pdf(x) = sin(2*x + \pi/2) = cos(2*x)
- return np.cos(2*x)
- def _cdf(self, x):
- return np.sin(x+np.pi/4)**2.0
- def _ppf(self, q):
- return np.arcsin(np.sqrt(q))-np.pi/4
- def _stats(self):
- return 0.0, np.pi*np.pi/16-0.5, 0.0, -2*(np.pi**4 - 96)/(np.pi*np.pi-8)**2
- def _entropy(self):
- return 1-np.log(2)
- anglit = anglit_gen(a=-np.pi/4, b=np.pi/4, name='anglit')
- class arcsine_gen(rv_continuous):
- r"""An arcsine continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `arcsine` is:
- .. math::
- f(x) = \frac{1}{\pi \sqrt{x (1-x)}}
- for :math:`0 < x < 1`.
- %(after_notes)s
- %(example)s
- """
- def _pdf(self, x):
- # arcsine.pdf(x) = 1/(pi*sqrt(x*(1-x)))
- with np.errstate(divide='ignore'):
- return 1.0/np.pi/np.sqrt(x*(1-x))
- def _cdf(self, x):
- return 2.0/np.pi*np.arcsin(np.sqrt(x))
- def _ppf(self, q):
- return np.sin(np.pi/2.0*q)**2.0
- def _stats(self):
- mu = 0.5
- mu2 = 1.0/8
- g1 = 0
- g2 = -3.0/2.0
- return mu, mu2, g1, g2
- def _entropy(self):
- return -0.24156447527049044468
- arcsine = arcsine_gen(a=0.0, b=1.0, name='arcsine')
- class FitDataError(ValueError):
- # This exception is raised by, for example, beta_gen.fit when both floc
- # and fscale are fixed and there are values in the data not in the open
- # interval (floc, floc+fscale).
- def __init__(self, distr, lower, upper):
- self.args = (
- "Invalid values in `data`. Maximum likelihood "
- "estimation with {distr!r} requires that {lower!r} < "
- "(x - loc)/scale < {upper!r} for each x in `data`.".format(
- distr=distr, lower=lower, upper=upper),
- )
- class FitSolverError(RuntimeError):
- # This exception is raised by, for example, beta_gen.fit when
- # optimize.fsolve returns with ier != 1.
- def __init__(self, mesg):
- emsg = "Solver for the MLE equations failed to converge: "
- emsg += mesg.replace('\n', '')
- self.args = (emsg,)
- def _beta_mle_a(a, b, n, s1):
- # The zeros of this function give the MLE for `a`, with
- # `b`, `n` and `s1` given. `s1` is the sum of the logs of
- # the data. `n` is the number of data points.
- psiab = sc.psi(a + b)
- func = s1 - n * (-psiab + sc.psi(a))
- return func
- def _beta_mle_ab(theta, n, s1, s2):
- # Zeros of this function are critical points of
- # the maximum likelihood function. Solving this system
- # for theta (which contains a and b) gives the MLE for a and b
- # given `n`, `s1` and `s2`. `s1` is the sum of the logs of the data,
- # and `s2` is the sum of the logs of 1 - data. `n` is the number
- # of data points.
- a, b = theta
- psiab = sc.psi(a + b)
- func = [s1 - n * (-psiab + sc.psi(a)),
- s2 - n * (-psiab + sc.psi(b))]
- return func
- class beta_gen(rv_continuous):
- r"""A beta continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `beta` is:
- .. math::
- f(x, a, b) = \frac{\Gamma(a+b) x^{a-1} (1-x)^{b-1}}
- {\Gamma(a) \Gamma(b)}
- for :math:`0 <= x <= 1`, :math:`a > 0`, :math:`b > 0`, where
- :math:`\Gamma` is the gamma function (`scipy.special.gamma`).
- `beta` takes :math:`a` and :math:`b` as shape parameters.
- %(after_notes)s
- %(example)s
- """
- def _rvs(self, a, b, size=None, random_state=None):
- return random_state.beta(a, b, size)
- def _pdf(self, x, a, b):
- # gamma(a+b) * x**(a-1) * (1-x)**(b-1)
- # beta.pdf(x, a, b) = ------------------------------------
- # gamma(a)*gamma(b)
- return _boost._beta_pdf(x, a, b)
- def _logpdf(self, x, a, b):
- lPx = sc.xlog1py(b - 1.0, -x) + sc.xlogy(a - 1.0, x)
- lPx -= sc.betaln(a, b)
- return lPx
- def _cdf(self, x, a, b):
- return _boost._beta_cdf(x, a, b)
- def _sf(self, x, a, b):
- return _boost._beta_sf(x, a, b)
- def _isf(self, x, a, b):
- return _boost._beta_isf(x, a, b)
- def _ppf(self, q, a, b):
- return _boost._beta_ppf(q, a, b)
- def _stats(self, a, b):
- return(
- _boost._beta_mean(a, b),
- _boost._beta_variance(a, b),
- _boost._beta_skewness(a, b),
- _boost._beta_kurtosis_excess(a, b))
- def _fitstart(self, data):
- g1 = _skew(data)
- g2 = _kurtosis(data)
- def func(x):
- a, b = x
- sk = 2*(b-a)*np.sqrt(a + b + 1) / (a + b + 2) / np.sqrt(a*b)
- ku = a**3 - a**2*(2*b-1) + b**2*(b+1) - 2*a*b*(b+2)
- ku /= a*b*(a+b+2)*(a+b+3)
- ku *= 6
- return [sk-g1, ku-g2]
- a, b = optimize.fsolve(func, (1.0, 1.0))
- return super()._fitstart(data, args=(a, b))
- @_call_super_mom
- @extend_notes_in_docstring(rv_continuous, notes="""\
- In the special case where `method="MLE"` and
- both `floc` and `fscale` are given, a
- `ValueError` is raised if any value `x` in `data` does not satisfy
- `floc < x < floc + fscale`.\n\n""")
- def fit(self, data, *args, **kwds):
- # Override rv_continuous.fit, so we can more efficiently handle the
- # case where floc and fscale are given.
- floc = kwds.get('floc', None)
- fscale = kwds.get('fscale', None)
- if floc is None or fscale is None:
- # do general fit
- return super().fit(data, *args, **kwds)
- # We already got these from kwds, so just pop them.
- kwds.pop('floc', None)
- kwds.pop('fscale', None)
- f0 = _get_fixed_fit_value(kwds, ['f0', 'fa', 'fix_a'])
- f1 = _get_fixed_fit_value(kwds, ['f1', 'fb', 'fix_b'])
- _remove_optimizer_parameters(kwds)
- if f0 is not None and f1 is not None:
- # This check is for consistency with `rv_continuous.fit`.
- raise ValueError("All parameters fixed. There is nothing to "
- "optimize.")
- # Special case: loc and scale are constrained, so we are fitting
- # just the shape parameters. This can be done much more efficiently
- # than the method used in `rv_continuous.fit`. (See the subsection
- # "Two unknown parameters" in the section "Maximum likelihood" of
- # the Wikipedia article on the Beta distribution for the formulas.)
- if not np.isfinite(data).all():
- raise RuntimeError("The data contains non-finite values.")
- # Normalize the data to the interval [0, 1].
- data = (np.ravel(data) - floc) / fscale
- if np.any(data <= 0) or np.any(data >= 1):
- raise FitDataError("beta", lower=floc, upper=floc + fscale)
- xbar = data.mean()
- if f0 is not None or f1 is not None:
- # One of the shape parameters is fixed.
- if f0 is not None:
- # The shape parameter a is fixed, so swap the parameters
- # and flip the data. We always solve for `a`. The result
- # will be swapped back before returning.
- b = f0
- data = 1 - data
- xbar = 1 - xbar
- else:
- b = f1
- # Initial guess for a. Use the formula for the mean of the beta
- # distribution, E[x] = a / (a + b), to generate a reasonable
- # starting point based on the mean of the data and the given
- # value of b.
- a = b * xbar / (1 - xbar)
- # Compute the MLE for `a` by solving _beta_mle_a.
- theta, info, ier, mesg = optimize.fsolve(
- _beta_mle_a, a,
- args=(b, len(data), np.log(data).sum()),
- full_output=True
- )
- if ier != 1:
- raise FitSolverError(mesg=mesg)
- a = theta[0]
- if f0 is not None:
- # The shape parameter a was fixed, so swap back the
- # parameters.
- a, b = b, a
- else:
- # Neither of the shape parameters is fixed.
- # s1 and s2 are used in the extra arguments passed to _beta_mle_ab
- # by optimize.fsolve.
- s1 = np.log(data).sum()
- s2 = sc.log1p(-data).sum()
- # Use the "method of moments" to estimate the initial
- # guess for a and b.
- fac = xbar * (1 - xbar) / data.var(ddof=0) - 1
- a = xbar * fac
- b = (1 - xbar) * fac
- # Compute the MLE for a and b by solving _beta_mle_ab.
- theta, info, ier, mesg = optimize.fsolve(
- _beta_mle_ab, [a, b],
- args=(len(data), s1, s2),
- full_output=True
- )
- if ier != 1:
- raise FitSolverError(mesg=mesg)
- a, b = theta
- return a, b, floc, fscale
- beta = beta_gen(a=0.0, b=1.0, name='beta')
- class betaprime_gen(rv_continuous):
- r"""A beta prime continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `betaprime` is:
- .. math::
- f(x, a, b) = \frac{x^{a-1} (1+x)^{-a-b}}{\beta(a, b)}
- for :math:`x >= 0`, :math:`a > 0`, :math:`b > 0`, where
- :math:`\beta(a, b)` is the beta function (see `scipy.special.beta`).
- `betaprime` takes ``a`` and ``b`` as shape parameters.
- %(after_notes)s
- %(example)s
- """
- _support_mask = rv_continuous._open_support_mask
- def _rvs(self, a, b, size=None, random_state=None):
- u1 = gamma.rvs(a, size=size, random_state=random_state)
- u2 = gamma.rvs(b, size=size, random_state=random_state)
- return u1 / u2
- def _pdf(self, x, a, b):
- # betaprime.pdf(x, a, b) = x**(a-1) * (1+x)**(-a-b) / beta(a, b)
- return np.exp(self._logpdf(x, a, b))
- def _logpdf(self, x, a, b):
- return sc.xlogy(a - 1.0, x) - sc.xlog1py(a + b, x) - sc.betaln(a, b)
- def _cdf(self, x, a, b):
- return sc.betainc(a, b, x/(1.+x))
- def _munp(self, n, a, b):
- if n == 1.0:
- return np.where(b > 1,
- a/(b-1.0),
- np.inf)
- elif n == 2.0:
- return np.where(b > 2,
- a*(a+1.0)/((b-2.0)*(b-1.0)),
- np.inf)
- elif n == 3.0:
- return np.where(b > 3,
- a*(a+1.0)*(a+2.0)/((b-3.0)*(b-2.0)*(b-1.0)),
- np.inf)
- elif n == 4.0:
- return np.where(b > 4,
- (a*(a + 1.0)*(a + 2.0)*(a + 3.0) /
- ((b - 4.0)*(b - 3.0)*(b - 2.0)*(b - 1.0))),
- np.inf)
- else:
- raise NotImplementedError
- betaprime = betaprime_gen(a=0.0, name='betaprime')
- class bradford_gen(rv_continuous):
- r"""A Bradford continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `bradford` is:
- .. math::
- f(x, c) = \frac{c}{\log(1+c) (1+cx)}
- for :math:`0 <= x <= 1` and :math:`c > 0`.
- `bradford` takes ``c`` as a shape parameter for :math:`c`.
- %(after_notes)s
- %(example)s
- """
- def _pdf(self, x, c):
- # bradford.pdf(x, c) = c / (k * (1+c*x))
- return c / (c*x + 1.0) / sc.log1p(c)
- def _cdf(self, x, c):
- return sc.log1p(c*x) / sc.log1p(c)
- def _ppf(self, q, c):
- return sc.expm1(q * sc.log1p(c)) / c
- def _stats(self, c, moments='mv'):
- k = np.log(1.0+c)
- mu = (c-k)/(c*k)
- mu2 = ((c+2.0)*k-2.0*c)/(2*c*k*k)
- g1 = None
- g2 = None
- if 's' in moments:
- g1 = np.sqrt(2)*(12*c*c-9*c*k*(c+2)+2*k*k*(c*(c+3)+3))
- g1 /= np.sqrt(c*(c*(k-2)+2*k))*(3*c*(k-2)+6*k)
- if 'k' in moments:
- g2 = (c**3*(k-3)*(k*(3*k-16)+24)+12*k*c*c*(k-4)*(k-3) +
- 6*c*k*k*(3*k-14) + 12*k**3)
- g2 /= 3*c*(c*(k-2)+2*k)**2
- return mu, mu2, g1, g2
- def _entropy(self, c):
- k = np.log(1+c)
- return k/2.0 - np.log(c/k)
- bradford = bradford_gen(a=0.0, b=1.0, name='bradford')
- class burr_gen(rv_continuous):
- r"""A Burr (Type III) continuous random variable.
- %(before_notes)s
- See Also
- --------
- fisk : a special case of either `burr` or `burr12` with ``d=1``
- burr12 : Burr Type XII distribution
- mielke : Mielke Beta-Kappa / Dagum distribution
- Notes
- -----
- The probability density function for `burr` is:
- .. math::
- f(x, c, d) = c d x^{-c - 1} / (1 + x^{-c})^{d + 1}
- for :math:`x >= 0` and :math:`c, d > 0`.
- `burr` takes :math:`c` and :math:`d` as shape parameters.
- This is the PDF corresponding to the third CDF given in Burr's list;
- specifically, it is equation (11) in Burr's paper [1]_. The distribution
- is also commonly referred to as the Dagum distribution [2]_. If the
- parameter :math:`c < 1` then the mean of the distribution does not
- exist and if :math:`c < 2` the variance does not exist [2]_.
- The PDF is finite at the left endpoint :math:`x = 0` if :math:`c * d >= 1`.
- %(after_notes)s
- References
- ----------
- .. [1] Burr, I. W. "Cumulative frequency functions", Annals of
- Mathematical Statistics, 13(2), pp 215-232 (1942).
- .. [2] https://en.wikipedia.org/wiki/Dagum_distribution
- .. [3] Kleiber, Christian. "A guide to the Dagum distributions."
- Modeling Income Distributions and Lorenz Curves pp 97-117 (2008).
- %(example)s
- """
- # Do not set _support_mask to rv_continuous._open_support_mask
- # Whether the left-hand endpoint is suitable for pdf evaluation is dependent
- # on the values of c and d: if c*d >= 1, the pdf is finite, otherwise infinite.
- def _pdf(self, x, c, d):
- # burr.pdf(x, c, d) = c * d * x**(-c-1) * (1+x**(-c))**(-d-1)
- output = _lazywhere(x == 0, [x, c, d],
- lambda x_, c_, d_: c_ * d_ * (x_**(c_*d_-1)) / (1 + x_**c_),
- f2 = lambda x_, c_, d_: (c_ * d_ * (x_ ** (-c_ - 1.0)) /
- ((1 + x_ ** (-c_)) ** (d_ + 1.0))))
- if output.ndim == 0:
- return output[()]
- return output
- def _logpdf(self, x, c, d):
- output = _lazywhere(
- x == 0, [x, c, d],
- lambda x_, c_, d_: (np.log(c_) + np.log(d_) + sc.xlogy(c_*d_ - 1, x_)
- - (d_+1) * sc.log1p(x_**(c_))),
- f2 = lambda x_, c_, d_: (np.log(c_) + np.log(d_)
- + sc.xlogy(-c_ - 1, x_)
- - sc.xlog1py(d_+1, x_**(-c_))))
- if output.ndim == 0:
- return output[()]
- return output
- def _cdf(self, x, c, d):
- return (1 + x**(-c))**(-d)
- def _logcdf(self, x, c, d):
- return sc.log1p(x**(-c)) * (-d)
- def _sf(self, x, c, d):
- return np.exp(self._logsf(x, c, d))
- def _logsf(self, x, c, d):
- return np.log1p(- (1 + x**(-c))**(-d))
- def _ppf(self, q, c, d):
- return (q**(-1.0/d) - 1)**(-1.0/c)
- def _stats(self, c, d):
- nc = np.arange(1, 5).reshape(4,1) / c
- #ek is the kth raw moment, e1 is the mean e2-e1**2 variance etc.
- e1, e2, e3, e4 = sc.beta(d + nc, 1. - nc) * d
- mu = np.where(c > 1.0, e1, np.nan)
- mu2_if_c = e2 - mu**2
- mu2 = np.where(c > 2.0, mu2_if_c, np.nan)
- g1 = _lazywhere(
- c > 3.0,
- (c, e1, e2, e3, mu2_if_c),
- lambda c, e1, e2, e3, mu2_if_c: (e3 - 3*e2*e1 + 2*e1**3) / np.sqrt((mu2_if_c)**3),
- fillvalue=np.nan)
- g2 = _lazywhere(
- c > 4.0,
- (c, e1, e2, e3, e4, mu2_if_c),
- lambda c, e1, e2, e3, e4, mu2_if_c: (
- ((e4 - 4*e3*e1 + 6*e2*e1**2 - 3*e1**4) / mu2_if_c**2) - 3),
- fillvalue=np.nan)
- if np.ndim(c) == 0:
- return mu.item(), mu2.item(), g1.item(), g2.item()
- return mu, mu2, g1, g2
- def _munp(self, n, c, d):
- def __munp(n, c, d):
- nc = 1. * n / c
- return d * sc.beta(1.0 - nc, d + nc)
- n, c, d = np.asarray(n), np.asarray(c), np.asarray(d)
- return _lazywhere((c > n) & (n == n) & (d == d), (c, d, n),
- lambda c, d, n: __munp(n, c, d),
- np.nan)
- burr = burr_gen(a=0.0, name='burr')
- class burr12_gen(rv_continuous):
- r"""A Burr (Type XII) continuous random variable.
- %(before_notes)s
- See Also
- --------
- fisk : a special case of either `burr` or `burr12` with ``d=1``
- burr : Burr Type III distribution
- Notes
- -----
- The probability density function for `burr` is:
- .. math::
- f(x, c, d) = c d x^{c-1} / (1 + x^c)^{d + 1}
- for :math:`x >= 0` and :math:`c, d > 0`.
- `burr12` takes ``c`` and ``d`` as shape parameters for :math:`c`
- and :math:`d`.
- This is the PDF corresponding to the twelfth CDF given in Burr's list;
- specifically, it is equation (20) in Burr's paper [1]_.
- %(after_notes)s
- The Burr type 12 distribution is also sometimes referred to as
- the Singh-Maddala distribution from NIST [2]_.
- References
- ----------
- .. [1] Burr, I. W. "Cumulative frequency functions", Annals of
- Mathematical Statistics, 13(2), pp 215-232 (1942).
- .. [2] https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/b12pdf.htm
- .. [3] "Burr distribution",
- https://en.wikipedia.org/wiki/Burr_distribution
- %(example)s
- """
- def _pdf(self, x, c, d):
- # burr12.pdf(x, c, d) = c * d * x**(c-1) * (1+x**(c))**(-d-1)
- return np.exp(self._logpdf(x, c, d))
- def _logpdf(self, x, c, d):
- return np.log(c) + np.log(d) + sc.xlogy(c - 1, x) + sc.xlog1py(-d-1, x**c)
- def _cdf(self, x, c, d):
- return -sc.expm1(self._logsf(x, c, d))
- def _logcdf(self, x, c, d):
- return sc.log1p(-(1 + x**c)**(-d))
- def _sf(self, x, c, d):
- return np.exp(self._logsf(x, c, d))
- def _logsf(self, x, c, d):
- return sc.xlog1py(-d, x**c)
- def _ppf(self, q, c, d):
- # The following is an implementation of
- # ((1 - q)**(-1.0/d) - 1)**(1.0/c)
- # that does a better job handling small values of q.
- return sc.expm1(-1/d * sc.log1p(-q))**(1/c)
- def _munp(self, n, c, d):
- nc = 1. * n / c
- return d * sc.beta(1.0 + nc, d - nc)
- burr12 = burr12_gen(a=0.0, name='burr12')
- class fisk_gen(burr_gen):
- r"""A Fisk continuous random variable.
- The Fisk distribution is also known as the log-logistic distribution.
- %(before_notes)s
- See Also
- --------
- burr
- Notes
- -----
- The probability density function for `fisk` is:
- .. math::
- f(x, c) = c x^{-c-1} (1 + x^{-c})^{-2}
- for :math:`x >= 0` and :math:`c > 0`.
- `fisk` takes ``c`` as a shape parameter for :math:`c`.
- `fisk` is a special case of `burr` or `burr12` with ``d=1``.
- %(after_notes)s
- %(example)s
- """
- def _pdf(self, x, c):
- # fisk.pdf(x, c) = c * x**(-c-1) * (1 + x**(-c))**(-2)
- return burr._pdf(x, c, 1.0)
- def _cdf(self, x, c):
- return burr._cdf(x, c, 1.0)
- def _sf(self, x, c):
- return burr._sf(x, c, 1.0)
- def _logpdf(self, x, c):
- # fisk.pdf(x, c) = c * x**(-c-1) * (1 + x**(-c))**(-2)
- return burr._logpdf(x, c, 1.0)
- def _logcdf(self, x, c):
- return burr._logcdf(x, c, 1.0)
- def _logsf(self, x, c):
- return burr._logsf(x, c, 1.0)
- def _ppf(self, x, c):
- return burr._ppf(x, c, 1.0)
- def _munp(self, n, c):
- return burr._munp(n, c, 1.0)
- def _stats(self, c):
- return burr._stats(c, 1.0)
- def _entropy(self, c):
- return 2 - np.log(c)
- fisk = fisk_gen(a=0.0, name='fisk')
- class cauchy_gen(rv_continuous):
- r"""A Cauchy continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `cauchy` is
- .. math::
- f(x) = \frac{1}{\pi (1 + x^2)}
- for a real number :math:`x`.
- %(after_notes)s
- %(example)s
- """
- def _pdf(self, x):
- # cauchy.pdf(x) = 1 / (pi * (1 + x**2))
- return 1.0/np.pi/(1.0+x*x)
- def _cdf(self, x):
- return 0.5 + 1.0/np.pi*np.arctan(x)
- def _ppf(self, q):
- return np.tan(np.pi*q-np.pi/2.0)
- def _sf(self, x):
- return 0.5 - 1.0/np.pi*np.arctan(x)
- def _isf(self, q):
- return np.tan(np.pi/2.0-np.pi*q)
- def _stats(self):
- return np.nan, np.nan, np.nan, np.nan
- def _entropy(self):
- return np.log(4*np.pi)
- def _fitstart(self, data, args=None):
- # Initialize ML guesses using quartiles instead of moments.
- p25, p50, p75 = np.percentile(data, [25, 50, 75])
- return p50, (p75 - p25)/2
- cauchy = cauchy_gen(name='cauchy')
- class chi_gen(rv_continuous):
- r"""A chi continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `chi` is:
- .. math::
- f(x, k) = \frac{1}{2^{k/2-1} \Gamma \left( k/2 \right)}
- x^{k-1} \exp \left( -x^2/2 \right)
- for :math:`x >= 0` and :math:`k > 0` (degrees of freedom, denoted ``df``
- in the implementation). :math:`\Gamma` is the gamma function
- (`scipy.special.gamma`).
- Special cases of `chi` are:
- - ``chi(1, loc, scale)`` is equivalent to `halfnorm`
- - ``chi(2, 0, scale)`` is equivalent to `rayleigh`
- - ``chi(3, 0, scale)`` is equivalent to `maxwell`
- `chi` takes ``df`` as a shape parameter.
- %(after_notes)s
- %(example)s
- """
- def _rvs(self, df, size=None, random_state=None):
- return np.sqrt(chi2.rvs(df, size=size, random_state=random_state))
- def _pdf(self, x, df):
- # x**(df-1) * exp(-x**2/2)
- # chi.pdf(x, df) = -------------------------
- # 2**(df/2-1) * gamma(df/2)
- return np.exp(self._logpdf(x, df))
- def _logpdf(self, x, df):
- l = np.log(2) - .5*np.log(2)*df - sc.gammaln(.5*df)
- return l + sc.xlogy(df - 1., x) - .5*x**2
- def _cdf(self, x, df):
- return sc.gammainc(.5*df, .5*x**2)
- def _sf(self, x, df):
- return sc.gammaincc(.5*df, .5*x**2)
- def _ppf(self, q, df):
- return np.sqrt(2*sc.gammaincinv(.5*df, q))
- def _isf(self, q, df):
- return np.sqrt(2*sc.gammainccinv(.5*df, q))
- def _stats(self, df):
- mu = np.sqrt(2)*sc.gamma(df/2.0+0.5)/sc.gamma(df/2.0)
- mu2 = df - mu*mu
- g1 = (2*mu**3.0 + mu*(1-2*df))/np.asarray(np.power(mu2, 1.5))
- g2 = 2*df*(1.0-df)-6*mu**4 + 4*mu**2 * (2*df-1)
- g2 /= np.asarray(mu2**2.0)
- return mu, mu2, g1, g2
- chi = chi_gen(a=0.0, name='chi')
- class chi2_gen(rv_continuous):
- r"""A chi-squared continuous random variable.
- For the noncentral chi-square distribution, see `ncx2`.
- %(before_notes)s
- See Also
- --------
- ncx2
- Notes
- -----
- The probability density function for `chi2` is:
- .. math::
- f(x, k) = \frac{1}{2^{k/2} \Gamma \left( k/2 \right)}
- x^{k/2-1} \exp \left( -x/2 \right)
- for :math:`x > 0` and :math:`k > 0` (degrees of freedom, denoted ``df``
- in the implementation).
- `chi2` takes ``df`` as a shape parameter.
- The chi-squared distribution is a special case of the gamma
- distribution, with gamma parameters ``a = df/2``, ``loc = 0`` and
- ``scale = 2``.
- %(after_notes)s
- %(example)s
- """
- def _rvs(self, df, size=None, random_state=None):
- return random_state.chisquare(df, size)
- def _pdf(self, x, df):
- # chi2.pdf(x, df) = 1 / (2*gamma(df/2)) * (x/2)**(df/2-1) * exp(-x/2)
- return np.exp(self._logpdf(x, df))
- def _logpdf(self, x, df):
- return sc.xlogy(df/2.-1, x) - x/2. - sc.gammaln(df/2.) - (np.log(2)*df)/2.
- def _cdf(self, x, df):
- return sc.chdtr(df, x)
- def _sf(self, x, df):
- return sc.chdtrc(df, x)
- def _isf(self, p, df):
- return sc.chdtri(df, p)
- def _ppf(self, p, df):
- return 2*sc.gammaincinv(df/2, p)
- def _stats(self, df):
- mu = df
- mu2 = 2*df
- g1 = 2*np.sqrt(2.0/df)
- g2 = 12.0/df
- return mu, mu2, g1, g2
- chi2 = chi2_gen(a=0.0, name='chi2')
- class cosine_gen(rv_continuous):
- r"""A cosine continuous random variable.
- %(before_notes)s
- Notes
- -----
- The cosine distribution is an approximation to the normal distribution.
- The probability density function for `cosine` is:
- .. math::
- f(x) = \frac{1}{2\pi} (1+\cos(x))
- for :math:`-\pi \le x \le \pi`.
- %(after_notes)s
- %(example)s
- """
- def _pdf(self, x):
- # cosine.pdf(x) = 1/(2*pi) * (1+cos(x))
- return 1.0/2/np.pi*(1+np.cos(x))
- def _cdf(self, x):
- return scu._cosine_cdf(x)
- def _sf(self, x):
- return scu._cosine_cdf(-x)
- def _ppf(self, p):
- return scu._cosine_invcdf(p)
- def _isf(self, p):
- return -scu._cosine_invcdf(p)
- def _stats(self):
- return 0.0, np.pi*np.pi/3.0-2.0, 0.0, -6.0*(np.pi**4-90)/(5.0*(np.pi*np.pi-6)**2)
- def _entropy(self):
- return np.log(4*np.pi)-1.0
- cosine = cosine_gen(a=-np.pi, b=np.pi, name='cosine')
- class dgamma_gen(rv_continuous):
- r"""A double gamma continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `dgamma` is:
- .. math::
- f(x, a) = \frac{1}{2\Gamma(a)} |x|^{a-1} \exp(-|x|)
- for a real number :math:`x` and :math:`a > 0`. :math:`\Gamma` is the
- gamma function (`scipy.special.gamma`).
- `dgamma` takes ``a`` as a shape parameter for :math:`a`.
- %(after_notes)s
- %(example)s
- """
- def _rvs(self, a, size=None, random_state=None):
- u = random_state.uniform(size=size)
- gm = gamma.rvs(a, size=size, random_state=random_state)
- return gm * np.where(u >= 0.5, 1, -1)
- def _pdf(self, x, a):
- # dgamma.pdf(x, a) = 1 / (2*gamma(a)) * abs(x)**(a-1) * exp(-abs(x))
- ax = abs(x)
- return 1.0/(2*sc.gamma(a))*ax**(a-1.0) * np.exp(-ax)
- def _logpdf(self, x, a):
- ax = abs(x)
- return sc.xlogy(a - 1.0, ax) - ax - np.log(2) - sc.gammaln(a)
- def _cdf(self, x, a):
- fac = 0.5*sc.gammainc(a, abs(x))
- return np.where(x > 0, 0.5 + fac, 0.5 - fac)
- def _sf(self, x, a):
- fac = 0.5*sc.gammainc(a, abs(x))
- return np.where(x > 0, 0.5-fac, 0.5+fac)
- def _ppf(self, q, a):
- fac = sc.gammainccinv(a, 1-abs(2*q-1))
- return np.where(q > 0.5, fac, -fac)
- def _stats(self, a):
- mu2 = a*(a+1.0)
- return 0.0, mu2, 0.0, (a+2.0)*(a+3.0)/mu2-3.0
- dgamma = dgamma_gen(name='dgamma')
- class dweibull_gen(rv_continuous):
- r"""A double Weibull continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `dweibull` is given by
- .. math::
- f(x, c) = c / 2 |x|^{c-1} \exp(-|x|^c)
- for a real number :math:`x` and :math:`c > 0`.
- `dweibull` takes ``c`` as a shape parameter for :math:`c`.
- %(after_notes)s
- %(example)s
- """
- def _rvs(self, c, size=None, random_state=None):
- u = random_state.uniform(size=size)
- w = weibull_min.rvs(c, size=size, random_state=random_state)
- return w * (np.where(u >= 0.5, 1, -1))
- def _pdf(self, x, c):
- # dweibull.pdf(x, c) = c / 2 * abs(x)**(c-1) * exp(-abs(x)**c)
- ax = abs(x)
- Px = c / 2.0 * ax**(c-1.0) * np.exp(-ax**c)
- return Px
- def _logpdf(self, x, c):
- ax = abs(x)
- return np.log(c) - np.log(2.0) + sc.xlogy(c - 1.0, ax) - ax**c
- def _cdf(self, x, c):
- Cx1 = 0.5 * np.exp(-abs(x)**c)
- return np.where(x > 0, 1 - Cx1, Cx1)
- def _ppf(self, q, c):
- fac = 2. * np.where(q <= 0.5, q, 1. - q)
- fac = np.power(-np.log(fac), 1.0 / c)
- return np.where(q > 0.5, fac, -fac)
- def _munp(self, n, c):
- return (1 - (n % 2)) * sc.gamma(1.0 + 1.0 * n / c)
- # since we know that all odd moments are zeros, return them at once.
- # returning Nones from _stats makes the public stats call _munp
- # so overall we're saving one or two gamma function evaluations here.
- def _stats(self, c):
- return 0, None, 0, None
- dweibull = dweibull_gen(name='dweibull')
- class expon_gen(rv_continuous):
- r"""An exponential continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `expon` is:
- .. math::
- f(x) = \exp(-x)
- for :math:`x \ge 0`.
- %(after_notes)s
- A common parameterization for `expon` is in terms of the rate parameter
- ``lambda``, such that ``pdf = lambda * exp(-lambda * x)``. This
- parameterization corresponds to using ``scale = 1 / lambda``.
- The exponential distribution is a special case of the gamma
- distributions, with gamma shape parameter ``a = 1``.
- %(example)s
- """
- def _rvs(self, size=None, random_state=None):
- return random_state.standard_exponential(size)
- def _pdf(self, x):
- # expon.pdf(x) = exp(-x)
- return np.exp(-x)
- def _logpdf(self, x):
- return -x
- def _cdf(self, x):
- return -sc.expm1(-x)
- def _ppf(self, q):
- return -sc.log1p(-q)
- def _sf(self, x):
- return np.exp(-x)
- def _logsf(self, x):
- return -x
- def _isf(self, q):
- return -np.log(q)
- def _stats(self):
- return 1.0, 1.0, 2.0, 6.0
- def _entropy(self):
- return 1.0
- @_call_super_mom
- @replace_notes_in_docstring(rv_continuous, notes="""\
- When `method='MLE'`,
- this function uses explicit formulas for the maximum likelihood
- estimation of the exponential distribution parameters, so the
- `optimizer`, `loc` and `scale` keyword arguments are
- ignored.\n\n""")
- def fit(self, data, *args, **kwds):
- if len(args) > 0:
- raise TypeError("Too many arguments.")
- floc = kwds.pop('floc', None)
- fscale = kwds.pop('fscale', None)
- _remove_optimizer_parameters(kwds)
- if floc is not None and fscale is not None:
- # This check is for consistency with `rv_continuous.fit`.
- raise ValueError("All parameters fixed. There is nothing to "
- "optimize.")
- data = np.asarray(data)
- if not np.isfinite(data).all():
- raise RuntimeError("The data contains non-finite values.")
- data_min = data.min()
- if floc is None:
- # ML estimate of the location is the minimum of the data.
- loc = data_min
- else:
- loc = floc
- if data_min < loc:
- # There are values that are less than the specified loc.
- raise FitDataError("expon", lower=floc, upper=np.inf)
- if fscale is None:
- # ML estimate of the scale is the shifted mean.
- scale = data.mean() - loc
- else:
- scale = fscale
- # We expect the return values to be floating point, so ensure it
- # by explicitly converting to float.
- return float(loc), float(scale)
- expon = expon_gen(a=0.0, name='expon')
- class exponnorm_gen(rv_continuous):
- r"""An exponentially modified Normal continuous random variable.
- Also known as the exponentially modified Gaussian distribution [1]_.
- %(before_notes)s
- Notes
- -----
- The probability density function for `exponnorm` is:
- .. math::
- f(x, K) = \frac{1}{2K} \exp\left(\frac{1}{2 K^2} - x / K \right)
- \text{erfc}\left(-\frac{x - 1/K}{\sqrt{2}}\right)
- where :math:`x` is a real number and :math:`K > 0`.
- It can be thought of as the sum of a standard normal random variable
- and an independent exponentially distributed random variable with rate
- ``1/K``.
- %(after_notes)s
- An alternative parameterization of this distribution (for example, in
- the Wikpedia article [1]_) involves three parameters, :math:`\mu`,
- :math:`\lambda` and :math:`\sigma`.
- In the present parameterization this corresponds to having ``loc`` and
- ``scale`` equal to :math:`\mu` and :math:`\sigma`, respectively, and
- shape parameter :math:`K = 1/(\sigma\lambda)`.
- .. versionadded:: 0.16.0
- References
- ----------
- .. [1] Exponentially modified Gaussian distribution, Wikipedia,
- https://en.wikipedia.org/wiki/Exponentially_modified_Gaussian_distribution
- %(example)s
- """
- def _rvs(self, K, size=None, random_state=None):
- expval = random_state.standard_exponential(size) * K
- gval = random_state.standard_normal(size)
- return expval + gval
- def _pdf(self, x, K):
- return np.exp(self._logpdf(x, K))
- def _logpdf(self, x, K):
- invK = 1.0 / K
- exparg = invK * (0.5 * invK - x)
- return exparg + _norm_logcdf(x - invK) - np.log(K)
- def _cdf(self, x, K):
- invK = 1.0 / K
- expval = invK * (0.5 * invK - x)
- logprod = expval + _norm_logcdf(x - invK)
- return _norm_cdf(x) - np.exp(logprod)
- def _sf(self, x, K):
- invK = 1.0 / K
- expval = invK * (0.5 * invK - x)
- logprod = expval + _norm_logcdf(x - invK)
- return _norm_cdf(-x) + np.exp(logprod)
- def _stats(self, K):
- K2 = K * K
- opK2 = 1.0 + K2
- skw = 2 * K**3 * opK2**(-1.5)
- krt = 6.0 * K2 * K2 * opK2**(-2)
- return K, opK2, skw, krt
- exponnorm = exponnorm_gen(name='exponnorm')
- class exponweib_gen(rv_continuous):
- r"""An exponentiated Weibull continuous random variable.
- %(before_notes)s
- See Also
- --------
- weibull_min, numpy.random.Generator.weibull
- Notes
- -----
- The probability density function for `exponweib` is:
- .. math::
- f(x, a, c) = a c [1-\exp(-x^c)]^{a-1} \exp(-x^c) x^{c-1}
- and its cumulative distribution function is:
- .. math::
- F(x, a, c) = [1-\exp(-x^c)]^a
- for :math:`x > 0`, :math:`a > 0`, :math:`c > 0`.
- `exponweib` takes :math:`a` and :math:`c` as shape parameters:
- * :math:`a` is the exponentiation parameter,
- with the special case :math:`a=1` corresponding to the
- (non-exponentiated) Weibull distribution `weibull_min`.
- * :math:`c` is the shape parameter of the non-exponentiated Weibull law.
- %(after_notes)s
- References
- ----------
- https://en.wikipedia.org/wiki/Exponentiated_Weibull_distribution
- %(example)s
- """
- def _pdf(self, x, a, c):
- # exponweib.pdf(x, a, c) =
- # a * c * (1-exp(-x**c))**(a-1) * exp(-x**c)*x**(c-1)
- return np.exp(self._logpdf(x, a, c))
- def _logpdf(self, x, a, c):
- negxc = -x**c
- exm1c = -sc.expm1(negxc)
- logp = (np.log(a) + np.log(c) + sc.xlogy(a - 1.0, exm1c) +
- negxc + sc.xlogy(c - 1.0, x))
- return logp
- def _cdf(self, x, a, c):
- exm1c = -sc.expm1(-x**c)
- return exm1c**a
- def _ppf(self, q, a, c):
- return (-sc.log1p(-q**(1.0/a)))**np.asarray(1.0/c)
- exponweib = exponweib_gen(a=0.0, name='exponweib')
- class exponpow_gen(rv_continuous):
- r"""An exponential power continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `exponpow` is:
- .. math::
- f(x, b) = b x^{b-1} \exp(1 + x^b - \exp(x^b))
- for :math:`x \ge 0`, :math:`b > 0`. Note that this is a different
- distribution from the exponential power distribution that is also known
- under the names "generalized normal" or "generalized Gaussian".
- `exponpow` takes ``b`` as a shape parameter for :math:`b`.
- %(after_notes)s
- References
- ----------
- http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Exponentialpower.pdf
- %(example)s
- """
- def _pdf(self, x, b):
- # exponpow.pdf(x, b) = b * x**(b-1) * exp(1 + x**b - exp(x**b))
- return np.exp(self._logpdf(x, b))
- def _logpdf(self, x, b):
- xb = x**b
- f = 1 + np.log(b) + sc.xlogy(b - 1.0, x) + xb - np.exp(xb)
- return f
- def _cdf(self, x, b):
- return -sc.expm1(-sc.expm1(x**b))
- def _sf(self, x, b):
- return np.exp(-sc.expm1(x**b))
- def _isf(self, x, b):
- return (sc.log1p(-np.log(x)))**(1./b)
- def _ppf(self, q, b):
- return pow(sc.log1p(-sc.log1p(-q)), 1.0/b)
- exponpow = exponpow_gen(a=0.0, name='exponpow')
- class fatiguelife_gen(rv_continuous):
- r"""A fatigue-life (Birnbaum-Saunders) continuous random variable.
- %(before_notes)s
- Notes
- -----
- The probability density function for `fatiguelife` is:
- .. math::
- f(x, c) = \frac{x+1}{2c\sqrt{2\pi x^3}} \exp(-\frac{(x-1)^2}{2x c^2})
- for :math:`x >= 0` and :math:`c > 0`.
- `fatiguelife` takes ``c`` as a shape parameter for :math:`c`.
- %(after_notes)s
- References
- ----------
- .. [1] "Birnbaum-Saunders distribution",
- https://en.wikipedia.org/wiki/Birnbaum-Saunders_distribution
- %(example)s
- """
- _support_mask = rv_continuous._open_support_mask
- def _rvs(self, c, size=None, random_state=None):
- z = random_state.standard_normal(size)
- x = 0.5*c*z
- x2 = x*x
- t = 1.0 + 2*x2 + 2*x*np.sqrt(1 + x2)
- return t
- def _pdf(self, x, c):
- # fatiguelife.pdf(x, c) =
- # (x+1) / (2*c*sqrt(2*pi*x**3)) * exp(-(x-1)**2/(2*x*c**2))
- return np.exp(self._logpdf(x, c))
- def _logpdf(self, x, c):
- return (np.log(x+1) - (x-1)**2 / (2.0*x*c**2) - np.log(2*c) -
- 0.5*(np.log(2*np.pi) + 3*np.log(x)))
- def _cdf(self, x, c):
- return _norm_cdf(1.0 / c * (np.sqrt(x) - 1.0/np.sqrt(x)))
- def _ppf(self, q, c):
- tmp = c*sc.ndtri(q)
- return 0.25 * (tmp + np.sqrt(tmp**2 + 4))**