Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/scipy/stats/_continuous_distns.py : 30%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2#
3# Author: Travis Oliphant 2002-2011 with contributions from
4# SciPy Developers 2004-2011
5#
6import warnings
7from collections.abc import Iterable
8import ctypes
10import numpy as np
12from scipy._lib.doccer import (extend_notes_in_docstring,
13 replace_notes_in_docstring)
14from scipy._lib._ccallback import LowLevelCallable
15from scipy import optimize
16from scipy import integrate
17from scipy import interpolate
18import scipy.special as sc
19import scipy.special._ufuncs as scu
20from scipy._lib._util import _lazyselect, _lazywhere
21from . import _stats
22from ._rvs_sampling import rvs_ratio_uniforms
23from ._tukeylambda_stats import (tukeylambda_variance as _tlvar,
24 tukeylambda_kurtosis as _tlkurt)
25from ._distn_infrastructure import (get_distribution_names, _kurtosis,
26 _ncx2_cdf, _ncx2_log_pdf, _ncx2_pdf,
27 rv_continuous, _skew, valarray,
28 _get_fixed_fit_value, _check_shape)
29from ._ksstats import kolmogn, kolmognp, kolmogni
30from ._constants import (_XMIN, _EULER, _ZETA3, _XMAX, _LOGXMAX,
31 _SQRT_2_OVER_PI, _LOG_SQRT_2_OVER_PI)
33# In numpy 1.12 and above, np.power refuses to raise integers to negative
34# powers, and `np.float_power` is a new replacement.
35try:
36 float_power = np.float_power
37except AttributeError:
38 float_power = np.power
40def _remove_optimizer_parameters(kwds):
41 """
42 Remove the optimizer-related keyword arguments 'loc', 'scale' and
43 'optimizer' from `kwds`. Then check that `kwds` is empty, and
44 raise `TypeError("Unknown arguments: %s." % kwds)` if it is not.
46 This function is used in the fit method of distributions that override
47 the default method and do not use the default optimization code.
49 `kwds` is modified in-place.
50 """
51 kwds.pop('loc', None)
52 kwds.pop('scale', None)
53 kwds.pop('optimizer', None)
54 if kwds:
55 raise TypeError("Unknown arguments: %s." % kwds)
58## Kolmogorov-Smirnov one-sided and two-sided test statistics
59class ksone_gen(rv_continuous):
60 r"""Kolmogorov-Smirnov one-sided test statistic distribution.
62 This is the distribution of the one-sided Kolmogorov-Smirnov (KS)
63 statistics :math:`D_n^+` and :math:`D_n^-`
64 for a finite sample size ``n`` (the shape parameter).
66 %(before_notes)s
68 Notes
69 -----
70 :math:`D_n^+` and :math:`D_n^-` are given by
72 .. math::
74 D_n^+ &= \text{sup}_x (F_n(x) - F(x)),\\
75 D_n^- &= \text{sup}_x (F(x) - F_n(x)),\\
77 where :math:`F` is a continuous CDF and :math:`F_n` is an empirical CDF.
78 `ksone` describes the distribution under the null hypothesis of the KS test
79 that the empirical CDF corresponds to :math:`n` i.i.d. random variates
80 with CDF :math:`F`.
82 %(after_notes)s
84 See Also
85 --------
86 kstwobign, kstwo, kstest
88 References
89 ----------
90 .. [1] Birnbaum, Z. W. and Tingey, F.H. "One-sided confidence contours
91 for probability distribution functions", The Annals of Mathematical
92 Statistics, 22(4), pp 592-596 (1951).
94 %(example)s
96 """
97 def _pdf(self, x, n):
98 return -scu._smirnovp(n, x)
100 def _cdf(self, x, n):
101 return scu._smirnovc(n, x)
103 def _sf(self, x, n):
104 return sc.smirnov(n, x)
106 def _ppf(self, q, n):
107 return scu._smirnovci(n, q)
109 def _isf(self, q, n):
110 return sc.smirnovi(n, q)
113ksone = ksone_gen(a=0.0, b=1.0, name='ksone')
116class kstwo_gen(rv_continuous):
117 r"""Kolmogorov-Smirnov two-sided test statistic distribution.
119 This is the distribution of the two-sided Kolmogorov-Smirnov (KS)
120 statistic :math:`D_n` for a finite sample size ``n``
121 (the shape parameter).
123 %(before_notes)s
125 Notes
126 -----
127 :math:`D_n` is given by
129 .. math::
131 D_n &= \text{sup}_x |F_n(x) - F(x)|
133 where :math:`F` is a (continuous) CDF and :math:`F_n` is an empirical CDF.
134 `kstwo` describes the distribution under the null hypothesis of the KS test
135 that the empirical CDF corresponds to :math:`n` i.i.d. random variates
136 with CDF :math:`F`.
138 %(after_notes)s
140 See Also
141 --------
142 kstwobign, ksone, kstest
144 References
145 ----------
146 .. [1] Simard, R., L'Ecuyer, P. "Computing the Two-Sided
147 Kolmogorov-Smirnov Distribution", Journal of Statistical Software,
148 Vol 39, 11, 1-18 (2011).
150 %(example)s
152 """
153 def _get_support(self, n):
154 return (0.5/(n if not isinstance(n, Iterable) else np.asanyarray(n)),
155 1.0)
157 def _pdf(self, x, n):
158 return kolmognp(n, x)
160 def _cdf(self, x, n):
161 return kolmogn(n, x)
163 def _sf(self, x, n):
164 return kolmogn(n, x, cdf=False)
166 def _ppf(self, q, n):
167 return kolmogni(n, q, cdf=True)
169 def _isf(self, q, n):
170 return kolmogni(n, q, cdf=False)
173# Use the pdf, (not the ppf) to compute moments
174kstwo = kstwo_gen(momtype=0, a=0.0, b=1.0, name='kstwo')
177class kstwobign_gen(rv_continuous):
178 r"""Limiting distribution of scaled Kolmogorov-Smirnov two-sided test statistic.
180 This is the asymptotic distribution of the two-sided Kolmogorov-Smirnov
181 statistic :math:`\sqrt{n} D_n` that measures the maximum absolute
182 distance of the theoretical (continuous) CDF from the empirical CDF.
183 (see `kstest`).
185 %(before_notes)s
187 Notes
188 -----
189 :math:`\sqrt{n} D_n` is given by
191 .. math::
193 D_n = \text{sup}_x |F_n(x) - F(x)|
195 where :math:`F` is a continuous CDF and :math:`F_n` is an empirical CDF.
196 `kstwobign` describes the asymptotic distribution (i.e. the limit of
197 :math:`\sqrt{n} D_n`) under the null hypothesis of the KS test that the
198 empirical CDF corresponds to i.i.d. random variates with CDF :math:`F`.
200 %(after_notes)s
202 See Also
203 --------
204 ksone, kstwo, kstest
206 References
207 ----------
208 .. [1] Feller, W. "On the Kolmogorov-Smirnov Limit Theorems for Empirical
209 Distributions", Ann. Math. Statist. Vol 19, 177-189 (1948).
211 %(example)s
213 """
214 def _pdf(self, x):
215 return -scu._kolmogp(x)
217 def _cdf(self, x):
218 return scu._kolmogc(x)
220 def _sf(self, x):
221 return sc.kolmogorov(x)
223 def _ppf(self, q):
224 return scu._kolmogci(q)
226 def _isf(self, q):
227 return sc.kolmogi(q)
230kstwobign = kstwobign_gen(a=0.0, name='kstwobign')
233## Normal distribution
235# loc = mu, scale = std
236# Keep these implementations out of the class definition so they can be reused
237# by other distributions.
238_norm_pdf_C = np.sqrt(2*np.pi)
239_norm_pdf_logC = np.log(_norm_pdf_C)
242def _norm_pdf(x):
243 return np.exp(-x**2/2.0) / _norm_pdf_C
246def _norm_logpdf(x):
247 return -x**2 / 2.0 - _norm_pdf_logC
250def _norm_cdf(x):
251 return sc.ndtr(x)
254def _norm_logcdf(x):
255 return sc.log_ndtr(x)
258def _norm_ppf(q):
259 return sc.ndtri(q)
262def _norm_sf(x):
263 return _norm_cdf(-x)
266def _norm_logsf(x):
267 return _norm_logcdf(-x)
270def _norm_isf(q):
271 return -_norm_ppf(q)
274class norm_gen(rv_continuous):
275 r"""A normal continuous random variable.
277 The location (``loc``) keyword specifies the mean.
278 The scale (``scale``) keyword specifies the standard deviation.
280 %(before_notes)s
282 Notes
283 -----
284 The probability density function for `norm` is:
286 .. math::
288 f(x) = \frac{\exp(-x^2/2)}{\sqrt{2\pi}}
290 for a real number :math:`x`.
292 %(after_notes)s
294 %(example)s
296 """
297 def _rvs(self, size=None, random_state=None):
298 return random_state.standard_normal(size)
300 def _pdf(self, x):
301 # norm.pdf(x) = exp(-x**2/2)/sqrt(2*pi)
302 return _norm_pdf(x)
304 def _logpdf(self, x):
305 return _norm_logpdf(x)
307 def _cdf(self, x):
308 return _norm_cdf(x)
310 def _logcdf(self, x):
311 return _norm_logcdf(x)
313 def _sf(self, x):
314 return _norm_sf(x)
316 def _logsf(self, x):
317 return _norm_logsf(x)
319 def _ppf(self, q):
320 return _norm_ppf(q)
322 def _isf(self, q):
323 return _norm_isf(q)
325 def _stats(self):
326 return 0.0, 1.0, 0.0, 0.0
328 def _entropy(self):
329 return 0.5*(np.log(2*np.pi)+1)
331 @replace_notes_in_docstring(rv_continuous, notes="""\
332 This function uses explicit formulas for the maximum likelihood
333 estimation of the normal distribution parameters, so the
334 `optimizer` argument is ignored.\n\n""")
335 def fit(self, data, **kwds):
336 floc = kwds.pop('floc', None)
337 fscale = kwds.pop('fscale', None)
339 _remove_optimizer_parameters(kwds)
341 if floc is not None and fscale is not None:
342 # This check is for consistency with `rv_continuous.fit`.
343 # Without this check, this function would just return the
344 # parameters that were given.
345 raise ValueError("All parameters fixed. There is nothing to "
346 "optimize.")
348 data = np.asarray(data)
350 if not np.isfinite(data).all():
351 raise RuntimeError("The data contains non-finite values.")
353 if floc is None:
354 loc = data.mean()
355 else:
356 loc = floc
358 if fscale is None:
359 scale = np.sqrt(((data - loc)**2).mean())
360 else:
361 scale = fscale
363 return loc, scale
365 def _munp(self, n):
366 """
367 @returns Moments of standard normal distribution for integer n >= 0
369 See eq. 16 of https://arxiv.org/abs/1209.4340v2
370 """
371 if n % 2 == 0:
372 return sc.factorial2(n - 1)
373 else:
374 return 0.
377norm = norm_gen(name='norm')
380class alpha_gen(rv_continuous):
381 r"""An alpha continuous random variable.
383 %(before_notes)s
385 Notes
386 -----
387 The probability density function for `alpha` ([1]_, [2]_) is:
389 .. math::
391 f(x, a) = \frac{1}{x^2 \Phi(a) \sqrt{2\pi}} *
392 \exp(-\frac{1}{2} (a-1/x)^2)
394 where :math:`\Phi` is the normal CDF, :math:`x > 0`, and :math:`a > 0`.
396 `alpha` takes ``a`` as a shape parameter.
398 %(after_notes)s
400 References
401 ----------
402 .. [1] Johnson, Kotz, and Balakrishnan, "Continuous Univariate
403 Distributions, Volume 1", Second Edition, John Wiley and Sons,
404 p. 173 (1994).
405 .. [2] Anthony A. Salvia, "Reliability applications of the Alpha
406 Distribution", IEEE Transactions on Reliability, Vol. R-34,
407 No. 3, pp. 251-252 (1985).
409 %(example)s
411 """
412 _support_mask = rv_continuous._open_support_mask
414 def _pdf(self, x, a):
415 # alpha.pdf(x, a) = 1/(x**2*Phi(a)*sqrt(2*pi)) * exp(-1/2 * (a-1/x)**2)
416 return 1.0/(x**2)/_norm_cdf(a)*_norm_pdf(a-1.0/x)
418 def _logpdf(self, x, a):
419 return -2*np.log(x) + _norm_logpdf(a-1.0/x) - np.log(_norm_cdf(a))
421 def _cdf(self, x, a):
422 return _norm_cdf(a-1.0/x) / _norm_cdf(a)
424 def _ppf(self, q, a):
425 return 1.0/np.asarray(a-sc.ndtri(q*_norm_cdf(a)))
427 def _stats(self, a):
428 return [np.inf]*2 + [np.nan]*2
431alpha = alpha_gen(a=0.0, name='alpha')
434class anglit_gen(rv_continuous):
435 r"""An anglit continuous random variable.
437 %(before_notes)s
439 Notes
440 -----
441 The probability density function for `anglit` is:
443 .. math::
445 f(x) = \sin(2x + \pi/2) = \cos(2x)
447 for :math:`-\pi/4 \le x \le \pi/4`.
449 %(after_notes)s
451 %(example)s
453 """
454 def _pdf(self, x):
455 # anglit.pdf(x) = sin(2*x + \pi/2) = cos(2*x)
456 return np.cos(2*x)
458 def _cdf(self, x):
459 return np.sin(x+np.pi/4)**2.0
461 def _ppf(self, q):
462 return np.arcsin(np.sqrt(q))-np.pi/4
464 def _stats(self):
465 return 0.0, np.pi*np.pi/16-0.5, 0.0, -2*(np.pi**4 - 96)/(np.pi*np.pi-8)**2
467 def _entropy(self):
468 return 1-np.log(2)
471anglit = anglit_gen(a=-np.pi/4, b=np.pi/4, name='anglit')
474class arcsine_gen(rv_continuous):
475 r"""An arcsine continuous random variable.
477 %(before_notes)s
479 Notes
480 -----
481 The probability density function for `arcsine` is:
483 .. math::
485 f(x) = \frac{1}{\pi \sqrt{x (1-x)}}
487 for :math:`0 < x < 1`.
489 %(after_notes)s
491 %(example)s
493 """
494 def _pdf(self, x):
495 # arcsine.pdf(x) = 1/(pi*sqrt(x*(1-x)))
496 return 1.0/np.pi/np.sqrt(x*(1-x))
498 def _cdf(self, x):
499 return 2.0/np.pi*np.arcsin(np.sqrt(x))
501 def _ppf(self, q):
502 return np.sin(np.pi/2.0*q)**2.0
504 def _stats(self):
505 mu = 0.5
506 mu2 = 1.0/8
507 g1 = 0
508 g2 = -3.0/2.0
509 return mu, mu2, g1, g2
511 def _entropy(self):
512 return -0.24156447527049044468
515arcsine = arcsine_gen(a=0.0, b=1.0, name='arcsine')
518class FitDataError(ValueError):
519 # This exception is raised by, for example, beta_gen.fit when both floc
520 # and fscale are fixed and there are values in the data not in the open
521 # interval (floc, floc+fscale).
522 def __init__(self, distr, lower, upper):
523 self.args = (
524 "Invalid values in `data`. Maximum likelihood "
525 "estimation with {distr!r} requires that {lower!r} < x "
526 "< {upper!r} for each x in `data`.".format(
527 distr=distr, lower=lower, upper=upper),
528 )
531class FitSolverError(RuntimeError):
532 # This exception is raised by, for example, beta_gen.fit when
533 # optimize.fsolve returns with ier != 1.
534 def __init__(self, mesg):
535 emsg = "Solver for the MLE equations failed to converge: "
536 emsg += mesg.replace('\n', '')
537 self.args = (emsg,)
540def _beta_mle_a(a, b, n, s1):
541 # The zeros of this function give the MLE for `a`, with
542 # `b`, `n` and `s1` given. `s1` is the sum of the logs of
543 # the data. `n` is the number of data points.
544 psiab = sc.psi(a + b)
545 func = s1 - n * (-psiab + sc.psi(a))
546 return func
549def _beta_mle_ab(theta, n, s1, s2):
550 # Zeros of this function are critical points of
551 # the maximum likelihood function. Solving this system
552 # for theta (which contains a and b) gives the MLE for a and b
553 # given `n`, `s1` and `s2`. `s1` is the sum of the logs of the data,
554 # and `s2` is the sum of the logs of 1 - data. `n` is the number
555 # of data points.
556 a, b = theta
557 psiab = sc.psi(a + b)
558 func = [s1 - n * (-psiab + sc.psi(a)),
559 s2 - n * (-psiab + sc.psi(b))]
560 return func
563class beta_gen(rv_continuous):
564 r"""A beta continuous random variable.
566 %(before_notes)s
568 Notes
569 -----
570 The probability density function for `beta` is:
572 .. math::
574 f(x, a, b) = \frac{\Gamma(a+b) x^{a-1} (1-x)^{b-1}}
575 {\Gamma(a) \Gamma(b)}
577 for :math:`0 <= x <= 1`, :math:`a > 0`, :math:`b > 0`, where
578 :math:`\Gamma` is the gamma function (`scipy.special.gamma`).
580 `beta` takes :math:`a` and :math:`b` as shape parameters.
582 %(after_notes)s
584 %(example)s
586 """
587 def _rvs(self, a, b, size=None, random_state=None):
588 return random_state.beta(a, b, size)
590 def _pdf(self, x, a, b):
591 # gamma(a+b) * x**(a-1) * (1-x)**(b-1)
592 # beta.pdf(x, a, b) = ------------------------------------
593 # gamma(a)*gamma(b)
594 return np.exp(self._logpdf(x, a, b))
596 def _logpdf(self, x, a, b):
597 lPx = sc.xlog1py(b - 1.0, -x) + sc.xlogy(a - 1.0, x)
598 lPx -= sc.betaln(a, b)
599 return lPx
601 def _cdf(self, x, a, b):
602 return sc.btdtr(a, b, x)
604 def _ppf(self, q, a, b):
605 return sc.btdtri(a, b, q)
607 def _stats(self, a, b):
608 mn = a*1.0 / (a + b)
609 var = (a*b*1.0)/(a+b+1.0)/(a+b)**2.0
610 g1 = 2.0*(b-a)*np.sqrt((1.0+a+b)/(a*b)) / (2+a+b)
611 g2 = 6.0*(a**3 + a**2*(1-2*b) + b**2*(1+b) - 2*a*b*(2+b))
612 g2 /= a*b*(a+b+2)*(a+b+3)
613 return mn, var, g1, g2
615 def _fitstart(self, data):
616 g1 = _skew(data)
617 g2 = _kurtosis(data)
619 def func(x):
620 a, b = x
621 sk = 2*(b-a)*np.sqrt(a + b + 1) / (a + b + 2) / np.sqrt(a*b)
622 ku = a**3 - a**2*(2*b-1) + b**2*(b+1) - 2*a*b*(b+2)
623 ku /= a*b*(a+b+2)*(a+b+3)
624 ku *= 6
625 return [sk-g1, ku-g2]
626 a, b = optimize.fsolve(func, (1.0, 1.0))
627 return super(beta_gen, self)._fitstart(data, args=(a, b))
629 @extend_notes_in_docstring(rv_continuous, notes="""\
630 In the special case where both `floc` and `fscale` are given, a
631 `ValueError` is raised if any value `x` in `data` does not satisfy
632 `floc < x < floc + fscale`.\n\n""")
633 def fit(self, data, *args, **kwds):
634 # Override rv_continuous.fit, so we can more efficiently handle the
635 # case where floc and fscale are given.
637 floc = kwds.get('floc', None)
638 fscale = kwds.get('fscale', None)
640 if floc is None or fscale is None:
641 # do general fit
642 return super(beta_gen, self).fit(data, *args, **kwds)
644 # We already got these from kwds, so just pop them.
645 kwds.pop('floc', None)
646 kwds.pop('fscale', None)
648 f0 = _get_fixed_fit_value(kwds, ['f0', 'fa', 'fix_a'])
649 f1 = _get_fixed_fit_value(kwds, ['f1', 'fb', 'fix_b'])
651 _remove_optimizer_parameters(kwds)
653 if f0 is not None and f1 is not None:
654 # This check is for consistency with `rv_continuous.fit`.
655 raise ValueError("All parameters fixed. There is nothing to "
656 "optimize.")
658 # Special case: loc and scale are constrained, so we are fitting
659 # just the shape parameters. This can be done much more efficiently
660 # than the method used in `rv_continuous.fit`. (See the subsection
661 # "Two unknown parameters" in the section "Maximum likelihood" of
662 # the Wikipedia article on the Beta distribution for the formulas.)
664 if not np.isfinite(data).all():
665 raise RuntimeError("The data contains non-finite values.")
667 # Normalize the data to the interval [0, 1].
668 data = (np.ravel(data) - floc) / fscale
669 if np.any(data <= 0) or np.any(data >= 1):
670 raise FitDataError("beta", lower=floc, upper=floc + fscale)
672 xbar = data.mean()
674 if f0 is not None or f1 is not None:
675 # One of the shape parameters is fixed.
677 if f0 is not None:
678 # The shape parameter a is fixed, so swap the parameters
679 # and flip the data. We always solve for `a`. The result
680 # will be swapped back before returning.
681 b = f0
682 data = 1 - data
683 xbar = 1 - xbar
684 else:
685 b = f1
687 # Initial guess for a. Use the formula for the mean of the beta
688 # distribution, E[x] = a / (a + b), to generate a reasonable
689 # starting point based on the mean of the data and the given
690 # value of b.
691 a = b * xbar / (1 - xbar)
693 # Compute the MLE for `a` by solving _beta_mle_a.
694 theta, info, ier, mesg = optimize.fsolve(
695 _beta_mle_a, a,
696 args=(b, len(data), np.log(data).sum()),
697 full_output=True
698 )
699 if ier != 1:
700 raise FitSolverError(mesg=mesg)
701 a = theta[0]
703 if f0 is not None:
704 # The shape parameter a was fixed, so swap back the
705 # parameters.
706 a, b = b, a
708 else:
709 # Neither of the shape parameters is fixed.
711 # s1 and s2 are used in the extra arguments passed to _beta_mle_ab
712 # by optimize.fsolve.
713 s1 = np.log(data).sum()
714 s2 = sc.log1p(-data).sum()
716 # Use the "method of moments" to estimate the initial
717 # guess for a and b.
718 fac = xbar * (1 - xbar) / data.var(ddof=0) - 1
719 a = xbar * fac
720 b = (1 - xbar) * fac
722 # Compute the MLE for a and b by solving _beta_mle_ab.
723 theta, info, ier, mesg = optimize.fsolve(
724 _beta_mle_ab, [a, b],
725 args=(len(data), s1, s2),
726 full_output=True
727 )
728 if ier != 1:
729 raise FitSolverError(mesg=mesg)
730 a, b = theta
732 return a, b, floc, fscale
735beta = beta_gen(a=0.0, b=1.0, name='beta')
738class betaprime_gen(rv_continuous):
739 r"""A beta prime continuous random variable.
741 %(before_notes)s
743 Notes
744 -----
745 The probability density function for `betaprime` is:
747 .. math::
749 f(x, a, b) = \frac{x^{a-1} (1+x)^{-a-b}}{\beta(a, b)}
751 for :math:`x >= 0`, :math:`a > 0`, :math:`b > 0`, where
752 :math:`\beta(a, b)` is the beta function (see `scipy.special.beta`).
754 `betaprime` takes ``a`` and ``b`` as shape parameters.
756 %(after_notes)s
758 %(example)s
760 """
761 _support_mask = rv_continuous._open_support_mask
763 def _rvs(self, a, b, size=None, random_state=None):
764 u1 = gamma.rvs(a, size=size, random_state=random_state)
765 u2 = gamma.rvs(b, size=size, random_state=random_state)
766 return u1 / u2
768 def _pdf(self, x, a, b):
769 # betaprime.pdf(x, a, b) = x**(a-1) * (1+x)**(-a-b) / beta(a, b)
770 return np.exp(self._logpdf(x, a, b))
772 def _logpdf(self, x, a, b):
773 return sc.xlogy(a - 1.0, x) - sc.xlog1py(a + b, x) - sc.betaln(a, b)
775 def _cdf(self, x, a, b):
776 return sc.betainc(a, b, x/(1.+x))
778 def _munp(self, n, a, b):
779 if n == 1.0:
780 return np.where(b > 1,
781 a/(b-1.0),
782 np.inf)
783 elif n == 2.0:
784 return np.where(b > 2,
785 a*(a+1.0)/((b-2.0)*(b-1.0)),
786 np.inf)
787 elif n == 3.0:
788 return np.where(b > 3,
789 a*(a+1.0)*(a+2.0)/((b-3.0)*(b-2.0)*(b-1.0)),
790 np.inf)
791 elif n == 4.0:
792 return np.where(b > 4,
793 (a*(a + 1.0)*(a + 2.0)*(a + 3.0) /
794 ((b - 4.0)*(b - 3.0)*(b - 2.0)*(b - 1.0))),
795 np.inf)
796 else:
797 raise NotImplementedError
800betaprime = betaprime_gen(a=0.0, name='betaprime')
803class bradford_gen(rv_continuous):
804 r"""A Bradford continuous random variable.
806 %(before_notes)s
808 Notes
809 -----
810 The probability density function for `bradford` is:
812 .. math::
814 f(x, c) = \frac{c}{\log(1+c) (1+cx)}
816 for :math:`0 <= x <= 1` and :math:`c > 0`.
818 `bradford` takes ``c`` as a shape parameter for :math:`c`.
820 %(after_notes)s
822 %(example)s
824 """
825 def _pdf(self, x, c):
826 # bradford.pdf(x, c) = c / (k * (1+c*x))
827 return c / (c*x + 1.0) / sc.log1p(c)
829 def _cdf(self, x, c):
830 return sc.log1p(c*x) / sc.log1p(c)
832 def _ppf(self, q, c):
833 return sc.expm1(q * sc.log1p(c)) / c
835 def _stats(self, c, moments='mv'):
836 k = np.log(1.0+c)
837 mu = (c-k)/(c*k)
838 mu2 = ((c+2.0)*k-2.0*c)/(2*c*k*k)
839 g1 = None
840 g2 = None
841 if 's' in moments:
842 g1 = np.sqrt(2)*(12*c*c-9*c*k*(c+2)+2*k*k*(c*(c+3)+3))
843 g1 /= np.sqrt(c*(c*(k-2)+2*k))*(3*c*(k-2)+6*k)
844 if 'k' in moments:
845 g2 = (c**3*(k-3)*(k*(3*k-16)+24)+12*k*c*c*(k-4)*(k-3) +
846 6*c*k*k*(3*k-14) + 12*k**3)
847 g2 /= 3*c*(c*(k-2)+2*k)**2
848 return mu, mu2, g1, g2
850 def _entropy(self, c):
851 k = np.log(1+c)
852 return k/2.0 - np.log(c/k)
855bradford = bradford_gen(a=0.0, b=1.0, name='bradford')
858class burr_gen(rv_continuous):
859 r"""A Burr (Type III) continuous random variable.
861 %(before_notes)s
863 See Also
864 --------
865 fisk : a special case of either `burr` or `burr12` with ``d=1``
866 burr12 : Burr Type XII distribution
867 mielke : Mielke Beta-Kappa / Dagum distribution
869 Notes
870 -----
871 The probability density function for `burr` is:
873 .. math::
875 f(x, c, d) = c d x^{-c - 1} / (1 + x^{-c})^{d + 1}
877 for :math:`x >= 0` and :math:`c, d > 0`.
879 `burr` takes :math:`c` and :math:`d` as shape parameters.
881 This is the PDF corresponding to the third CDF given in Burr's list;
882 specifically, it is equation (11) in Burr's paper [1]_. The distribution
883 is also commonly referred to as the Dagum distribution [2]_. If the
884 parameter :math:`c < 1` then the mean of the distribution does not
885 exist and if :math:`c < 2` the variance does not exist [2]_.
886 The PDF is finite at the left endpoint :math:`x = 0` if :math:`c * d >= 1`.
888 %(after_notes)s
890 References
891 ----------
892 .. [1] Burr, I. W. "Cumulative frequency functions", Annals of
893 Mathematical Statistics, 13(2), pp 215-232 (1942).
894 .. [2] https://en.wikipedia.org/wiki/Dagum_distribution
895 .. [3] Kleiber, Christian. "A guide to the Dagum distributions."
896 Modeling Income Distributions and Lorenz Curves pp 97-117 (2008).
898 %(example)s
900 """
901 # Do not set _support_mask to rv_continuous._open_support_mask
902 # Whether the left-hand endpoint is suitable for pdf evaluation is dependent
903 # on the values of c and d: if c*d >= 1, the pdf is finite, otherwise infinite.
905 def _pdf(self, x, c, d):
906 # burr.pdf(x, c, d) = c * d * x**(-c-1) * (1+x**(-c))**(-d-1)
907 output = _lazywhere(x == 0, [x, c, d],
908 lambda x_, c_, d_: c_ * d_ * (x_**(c_*d_-1)) / (1 + x_**c_),
909 f2 = lambda x_, c_, d_: (c_ * d_ * (x_ ** (-c_ - 1.0)) /
910 ((1 + x_ ** (-c_)) ** (d_ + 1.0))))
911 if output.ndim == 0:
912 return output[()]
913 return output
915 def _logpdf(self, x, c, d):
916 output = _lazywhere(
917 x == 0, [x, c, d],
918 lambda x_, c_, d_: (np.log(c_) + np.log(d_) + sc.xlogy(c_*d_ - 1, x_)
919 - (d_+1) * sc.log1p(x_**(c_))),
920 f2 = lambda x_, c_, d_: (np.log(c_) + np.log(d_)
921 + sc.xlogy(-c_ - 1, x_)
922 - sc.xlog1py(d_+1, x_**(-c_))))
923 if output.ndim == 0:
924 return output[()]
925 return output
927 def _cdf(self, x, c, d):
928 return (1 + x**(-c))**(-d)
930 def _logcdf(self, x, c, d):
931 return sc.log1p(x**(-c)) * (-d)
933 def _sf(self, x, c, d):
934 return np.exp(self._logsf(x, c, d))
936 def _logsf(self, x, c, d):
937 return np.log1p(- (1 + x**(-c))**(-d))
939 def _ppf(self, q, c, d):
940 return (q**(-1.0/d) - 1)**(-1.0/c)
942 def _stats(self, c, d):
943 nc = np.arange(1, 5).reshape(4,1) / c
944 #ek is the kth raw moment, e1 is the mean e2-e1**2 variance etc.
945 e1, e2, e3, e4 = sc.beta(d + nc, 1. - nc) * d
946 mu = np.where(c > 1.0, e1, np.nan)
947 mu2_if_c = e2 - mu**2
948 mu2 = np.where(c > 2.0, mu2_if_c, np.nan)
949 g1 = _lazywhere(
950 c > 3.0,
951 (c, e1, e2, e3, mu2_if_c),
952 lambda c, e1, e2, e3, mu2_if_c: (e3 - 3*e2*e1 + 2*e1**3) / np.sqrt((mu2_if_c)**3),
953 fillvalue=np.nan)
954 g2 = _lazywhere(
955 c > 4.0,
956 (c, e1, e2, e3, e4, mu2_if_c),
957 lambda c, e1, e2, e3, e4, mu2_if_c: (
958 ((e4 - 4*e3*e1 + 6*e2*e1**2 - 3*e1**4) / mu2_if_c**2) - 3),
959 fillvalue=np.nan)
960 return mu, mu2, g1, g2
962 def _munp(self, n, c, d):
963 def __munp(n, c, d):
964 nc = 1. * n / c
965 return d * sc.beta(1.0 - nc, d + nc)
966 n, c, d = np.asarray(n), np.asarray(c), np.asarray(d)
967 return _lazywhere((c > n) & (n == n) & (d == d), (c, d, n),
968 lambda c, d, n: __munp(n, c, d),
969 np.nan)
972burr = burr_gen(a=0.0, name='burr')
975class burr12_gen(rv_continuous):
976 r"""A Burr (Type XII) continuous random variable.
978 %(before_notes)s
980 See Also
981 --------
982 fisk : a special case of either `burr` or `burr12` with ``d=1``
983 burr : Burr Type III distribution
985 Notes
986 -----
987 The probability density function for `burr` is:
989 .. math::
991 f(x, c, d) = c d x^{c-1} / (1 + x^c)^{d + 1}
993 for :math:`x >= 0` and :math:`c, d > 0`.
995 `burr12` takes ``c`` and ``d`` as shape parameters for :math:`c`
996 and :math:`d`.
998 This is the PDF corresponding to the twelfth CDF given in Burr's list;
999 specifically, it is equation (20) in Burr's paper [1]_.
1001 %(after_notes)s
1003 The Burr type 12 distribution is also sometimes referred to as
1004 the Singh-Maddala distribution from NIST [2]_.
1006 References
1007 ----------
1008 .. [1] Burr, I. W. "Cumulative frequency functions", Annals of
1009 Mathematical Statistics, 13(2), pp 215-232 (1942).
1011 .. [2] https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/b12pdf.htm
1013 .. [3] "Burr distribution",
1014 https://en.wikipedia.org/wiki/Burr_distribution
1016 %(example)s
1018 """
1019 def _pdf(self, x, c, d):
1020 # burr12.pdf(x, c, d) = c * d * x**(c-1) * (1+x**(c))**(-d-1)
1021 return np.exp(self._logpdf(x, c, d))
1023 def _logpdf(self, x, c, d):
1024 return np.log(c) + np.log(d) + sc.xlogy(c - 1, x) + sc.xlog1py(-d-1, x**c)
1026 def _cdf(self, x, c, d):
1027 return -sc.expm1(self._logsf(x, c, d))
1029 def _logcdf(self, x, c, d):
1030 return sc.log1p(-(1 + x**c)**(-d))
1032 def _sf(self, x, c, d):
1033 return np.exp(self._logsf(x, c, d))
1035 def _logsf(self, x, c, d):
1036 return sc.xlog1py(-d, x**c)
1038 def _ppf(self, q, c, d):
1039 # The following is an implementation of
1040 # ((1 - q)**(-1.0/d) - 1)**(1.0/c)
1041 # that does a better job handling small values of q.
1042 return sc.expm1(-1/d * sc.log1p(-q))**(1/c)
1044 def _munp(self, n, c, d):
1045 nc = 1. * n / c
1046 return d * sc.beta(1.0 + nc, d - nc)
1049burr12 = burr12_gen(a=0.0, name='burr12')
1052class fisk_gen(burr_gen):
1053 r"""A Fisk continuous random variable.
1055 The Fisk distribution is also known as the log-logistic distribution.
1057 %(before_notes)s
1059 Notes
1060 -----
1061 The probability density function for `fisk` is:
1063 .. math::
1065 f(x, c) = c x^{-c-1} (1 + x^{-c})^{-2}
1067 for :math:`x >= 0` and :math:`c > 0`.
1069 `fisk` takes ``c`` as a shape parameter for :math:`c`.
1071 `fisk` is a special case of `burr` or `burr12` with ``d=1``.
1073 %(after_notes)s
1075 See Also
1076 --------
1077 burr
1079 %(example)s
1081 """
1082 def _pdf(self, x, c):
1083 # fisk.pdf(x, c) = c * x**(-c-1) * (1 + x**(-c))**(-2)
1084 return burr._pdf(x, c, 1.0)
1086 def _cdf(self, x, c):
1087 return burr._cdf(x, c, 1.0)
1089 def _sf(self, x, c):
1090 return burr._sf(x, c, 1.0)
1092 def _logpdf(self, x, c):
1093 # fisk.pdf(x, c) = c * x**(-c-1) * (1 + x**(-c))**(-2)
1094 return burr._logpdf(x, c, 1.0)
1096 def _logcdf(self, x, c):
1097 return burr._logcdf(x, c, 1.0)
1099 def _logsf(self, x, c):
1100 return burr._logsf(x, c, 1.0)
1102 def _ppf(self, x, c):
1103 return burr._ppf(x, c, 1.0)
1105 def _munp(self, n, c):
1106 return burr._munp(n, c, 1.0)
1108 def _stats(self, c):
1109 return burr._stats(c, 1.0)
1111 def _entropy(self, c):
1112 return 2 - np.log(c)
1115fisk = fisk_gen(a=0.0, name='fisk')
1118# median = loc
1119class cauchy_gen(rv_continuous):
1120 r"""A Cauchy continuous random variable.
1122 %(before_notes)s
1124 Notes
1125 -----
1126 The probability density function for `cauchy` is
1128 .. math::
1130 f(x) = \frac{1}{\pi (1 + x^2)}
1132 for a real number :math:`x`.
1134 %(after_notes)s
1136 %(example)s
1138 """
1139 def _pdf(self, x):
1140 # cauchy.pdf(x) = 1 / (pi * (1 + x**2))
1141 return 1.0/np.pi/(1.0+x*x)
1143 def _cdf(self, x):
1144 return 0.5 + 1.0/np.pi*np.arctan(x)
1146 def _ppf(self, q):
1147 return np.tan(np.pi*q-np.pi/2.0)
1149 def _sf(self, x):
1150 return 0.5 - 1.0/np.pi*np.arctan(x)
1152 def _isf(self, q):
1153 return np.tan(np.pi/2.0-np.pi*q)
1155 def _stats(self):
1156 return np.nan, np.nan, np.nan, np.nan
1158 def _entropy(self):
1159 return np.log(4*np.pi)
1161 def _fitstart(self, data, args=None):
1162 # Initialize ML guesses using quartiles instead of moments.
1163 p25, p50, p75 = np.percentile(data, [25, 50, 75])
1164 return p50, (p75 - p25)/2
1167cauchy = cauchy_gen(name='cauchy')
1170class chi_gen(rv_continuous):
1171 r"""A chi continuous random variable.
1173 %(before_notes)s
1175 Notes
1176 -----
1177 The probability density function for `chi` is:
1179 .. math::
1181 f(x, k) = \frac{1}{2^{k/2-1} \Gamma \left( k/2 \right)}
1182 x^{k-1} \exp \left( -x^2/2 \right)
1184 for :math:`x >= 0` and :math:`k > 0` (degrees of freedom, denoted ``df``
1185 in the implementation). :math:`\Gamma` is the gamma function
1186 (`scipy.special.gamma`).
1188 Special cases of `chi` are:
1190 - ``chi(1, loc, scale)`` is equivalent to `halfnorm`
1191 - ``chi(2, 0, scale)`` is equivalent to `rayleigh`
1192 - ``chi(3, 0, scale)`` is equivalent to `maxwell`
1194 `chi` takes ``df`` as a shape parameter.
1196 %(after_notes)s
1198 %(example)s
1200 """
1202 def _rvs(self, df, size=None, random_state=None):
1203 return np.sqrt(chi2.rvs(df, size=size, random_state=random_state))
1205 def _pdf(self, x, df):
1206 # x**(df-1) * exp(-x**2/2)
1207 # chi.pdf(x, df) = -------------------------
1208 # 2**(df/2-1) * gamma(df/2)
1209 return np.exp(self._logpdf(x, df))
1211 def _logpdf(self, x, df):
1212 l = np.log(2) - .5*np.log(2)*df - sc.gammaln(.5*df)
1213 return l + sc.xlogy(df - 1., x) - .5*x**2
1215 def _cdf(self, x, df):
1216 return sc.gammainc(.5*df, .5*x**2)
1218 def _ppf(self, q, df):
1219 return np.sqrt(2*sc.gammaincinv(.5*df, q))
1221 def _stats(self, df):
1222 mu = np.sqrt(2)*sc.gamma(df/2.0+0.5)/sc.gamma(df/2.0)
1223 mu2 = df - mu*mu
1224 g1 = (2*mu**3.0 + mu*(1-2*df))/np.asarray(np.power(mu2, 1.5))
1225 g2 = 2*df*(1.0-df)-6*mu**4 + 4*mu**2 * (2*df-1)
1226 g2 /= np.asarray(mu2**2.0)
1227 return mu, mu2, g1, g2
1230chi = chi_gen(a=0.0, name='chi')
1233## Chi-squared (gamma-distributed with loc=0 and scale=2 and shape=df/2)
1234class chi2_gen(rv_continuous):
1235 r"""A chi-squared continuous random variable.
1237 %(before_notes)s
1239 Notes
1240 -----
1241 The probability density function for `chi2` is:
1243 .. math::
1245 f(x, k) = \frac{1}{2^{k/2} \Gamma \left( k/2 \right)}
1246 x^{k/2-1} \exp \left( -x/2 \right)
1248 for :math:`x > 0` and :math:`k > 0` (degrees of freedom, denoted ``df``
1249 in the implementation).
1251 `chi2` takes ``df`` as a shape parameter.
1253 %(after_notes)s
1255 %(example)s
1257 """
1258 def _rvs(self, df, size=None, random_state=None):
1259 return random_state.chisquare(df, size)
1261 def _pdf(self, x, df):
1262 # chi2.pdf(x, df) = 1 / (2*gamma(df/2)) * (x/2)**(df/2-1) * exp(-x/2)
1263 return np.exp(self._logpdf(x, df))
1265 def _logpdf(self, x, df):
1266 return sc.xlogy(df/2.-1, x) - x/2. - sc.gammaln(df/2.) - (np.log(2)*df)/2.
1268 def _cdf(self, x, df):
1269 return sc.chdtr(df, x)
1271 def _sf(self, x, df):
1272 return sc.chdtrc(df, x)
1274 def _isf(self, p, df):
1275 return sc.chdtri(df, p)
1277 def _ppf(self, p, df):
1278 return 2*sc.gammaincinv(df/2, p)
1280 def _stats(self, df):
1281 mu = df
1282 mu2 = 2*df
1283 g1 = 2*np.sqrt(2.0/df)
1284 g2 = 12.0/df
1285 return mu, mu2, g1, g2
1288chi2 = chi2_gen(a=0.0, name='chi2')
1291class cosine_gen(rv_continuous):
1292 r"""A cosine continuous random variable.
1294 %(before_notes)s
1296 Notes
1297 -----
1298 The cosine distribution is an approximation to the normal distribution.
1299 The probability density function for `cosine` is:
1301 .. math::
1303 f(x) = \frac{1}{2\pi} (1+\cos(x))
1305 for :math:`-\pi \le x \le \pi`.
1307 %(after_notes)s
1309 %(example)s
1311 """
1312 def _pdf(self, x):
1313 # cosine.pdf(x) = 1/(2*pi) * (1+cos(x))
1314 return 1.0/2/np.pi*(1+np.cos(x))
1316 def _cdf(self, x):
1317 return 1.0/2/np.pi*(np.pi + x + np.sin(x))
1319 def _stats(self):
1320 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)
1322 def _entropy(self):
1323 return np.log(4*np.pi)-1.0
1326cosine = cosine_gen(a=-np.pi, b=np.pi, name='cosine')
1329class dgamma_gen(rv_continuous):
1330 r"""A double gamma continuous random variable.
1332 %(before_notes)s
1334 Notes
1335 -----
1336 The probability density function for `dgamma` is:
1338 .. math::
1340 f(x, a) = \frac{1}{2\Gamma(a)} |x|^{a-1} \exp(-|x|)
1342 for a real number :math:`x` and :math:`a > 0`. :math:`\Gamma` is the
1343 gamma function (`scipy.special.gamma`).
1345 `dgamma` takes ``a`` as a shape parameter for :math:`a`.
1347 %(after_notes)s
1349 %(example)s
1351 """
1352 def _rvs(self, a, size=None, random_state=None):
1353 u = random_state.uniform(size=size)
1354 gm = gamma.rvs(a, size=size, random_state=random_state)
1355 return gm * np.where(u >= 0.5, 1, -1)
1357 def _pdf(self, x, a):
1358 # dgamma.pdf(x, a) = 1 / (2*gamma(a)) * abs(x)**(a-1) * exp(-abs(x))
1359 ax = abs(x)
1360 return 1.0/(2*sc.gamma(a))*ax**(a-1.0) * np.exp(-ax)
1362 def _logpdf(self, x, a):
1363 ax = abs(x)
1364 return sc.xlogy(a - 1.0, ax) - ax - np.log(2) - sc.gammaln(a)
1366 def _cdf(self, x, a):
1367 fac = 0.5*sc.gammainc(a, abs(x))
1368 return np.where(x > 0, 0.5 + fac, 0.5 - fac)
1370 def _sf(self, x, a):
1371 fac = 0.5*sc.gammainc(a, abs(x))
1372 return np.where(x > 0, 0.5-fac, 0.5+fac)
1374 def _ppf(self, q, a):
1375 fac = sc.gammainccinv(a, 1-abs(2*q-1))
1376 return np.where(q > 0.5, fac, -fac)
1378 def _stats(self, a):
1379 mu2 = a*(a+1.0)
1380 return 0.0, mu2, 0.0, (a+2.0)*(a+3.0)/mu2-3.0
1383dgamma = dgamma_gen(name='dgamma')
1386class dweibull_gen(rv_continuous):
1387 r"""A double Weibull continuous random variable.
1389 %(before_notes)s
1391 Notes
1392 -----
1393 The probability density function for `dweibull` is given by
1395 .. math::
1397 f(x, c) = c / 2 |x|^{c-1} \exp(-|x|^c)
1399 for a real number :math:`x` and :math:`c > 0`.
1401 `dweibull` takes ``c`` as a shape parameter for :math:`c`.
1403 %(after_notes)s
1405 %(example)s
1407 """
1408 def _rvs(self, c, size=None, random_state=None):
1409 u = random_state.uniform(size=size)
1410 w = weibull_min.rvs(c, size=size, random_state=random_state)
1411 return w * (np.where(u >= 0.5, 1, -1))
1413 def _pdf(self, x, c):
1414 # dweibull.pdf(x, c) = c / 2 * abs(x)**(c-1) * exp(-abs(x)**c)
1415 ax = abs(x)
1416 Px = c / 2.0 * ax**(c-1.0) * np.exp(-ax**c)
1417 return Px
1419 def _logpdf(self, x, c):
1420 ax = abs(x)
1421 return np.log(c) - np.log(2.0) + sc.xlogy(c - 1.0, ax) - ax**c
1423 def _cdf(self, x, c):
1424 Cx1 = 0.5 * np.exp(-abs(x)**c)
1425 return np.where(x > 0, 1 - Cx1, Cx1)
1427 def _ppf(self, q, c):
1428 fac = 2. * np.where(q <= 0.5, q, 1. - q)
1429 fac = np.power(-np.log(fac), 1.0 / c)
1430 return np.where(q > 0.5, fac, -fac)
1432 def _munp(self, n, c):
1433 return (1 - (n % 2)) * sc.gamma(1.0 + 1.0 * n / c)
1435 # since we know that all odd moments are zeros, return them at once.
1436 # returning Nones from _stats makes the public stats call _munp
1437 # so overall we're saving one or two gamma function evaluations here.
1438 def _stats(self, c):
1439 return 0, None, 0, None
1442dweibull = dweibull_gen(name='dweibull')
1445## Exponential (gamma distributed with a=1.0, loc=loc and scale=scale)
1446class expon_gen(rv_continuous):
1447 r"""An exponential continuous random variable.
1449 %(before_notes)s
1451 Notes
1452 -----
1453 The probability density function for `expon` is:
1455 .. math::
1457 f(x) = \exp(-x)
1459 for :math:`x \ge 0`.
1461 %(after_notes)s
1463 A common parameterization for `expon` is in terms of the rate parameter
1464 ``lambda``, such that ``pdf = lambda * exp(-lambda * x)``. This
1465 parameterization corresponds to using ``scale = 1 / lambda``.
1467 %(example)s
1469 """
1470 def _rvs(self, size=None, random_state=None):
1471 return random_state.standard_exponential(size)
1473 def _pdf(self, x):
1474 # expon.pdf(x) = exp(-x)
1475 return np.exp(-x)
1477 def _logpdf(self, x):
1478 return -x
1480 def _cdf(self, x):
1481 return -sc.expm1(-x)
1483 def _ppf(self, q):
1484 return -sc.log1p(-q)
1486 def _sf(self, x):
1487 return np.exp(-x)
1489 def _logsf(self, x):
1490 return -x
1492 def _isf(self, q):
1493 return -np.log(q)
1495 def _stats(self):
1496 return 1.0, 1.0, 2.0, 6.0
1498 def _entropy(self):
1499 return 1.0
1501 @replace_notes_in_docstring(rv_continuous, notes="""\
1502 This function uses explicit formulas for the maximum likelihood
1503 estimation of the exponential distribution parameters, so the
1504 `optimizer`, `loc` and `scale` keyword arguments are ignored.\n\n""")
1505 def fit(self, data, *args, **kwds):
1506 if len(args) > 0:
1507 raise TypeError("Too many arguments.")
1509 floc = kwds.pop('floc', None)
1510 fscale = kwds.pop('fscale', None)
1512 _remove_optimizer_parameters(kwds)
1514 if floc is not None and fscale is not None:
1515 # This check is for consistency with `rv_continuous.fit`.
1516 raise ValueError("All parameters fixed. There is nothing to "
1517 "optimize.")
1519 data = np.asarray(data)
1521 if not np.isfinite(data).all():
1522 raise RuntimeError("The data contains non-finite values.")
1524 data_min = data.min()
1526 if floc is None:
1527 # ML estimate of the location is the minimum of the data.
1528 loc = data_min
1529 else:
1530 loc = floc
1531 if data_min < loc:
1532 # There are values that are less than the specified loc.
1533 raise FitDataError("expon", lower=floc, upper=np.inf)
1535 if fscale is None:
1536 # ML estimate of the scale is the shifted mean.
1537 scale = data.mean() - loc
1538 else:
1539 scale = fscale
1541 # We expect the return values to be floating point, so ensure it
1542 # by explicitly converting to float.
1543 return float(loc), float(scale)
1546expon = expon_gen(a=0.0, name='expon')
1549## Exponentially Modified Normal (exponential distribution
1550## convolved with a Normal).
1551## This is called an exponentially modified gaussian on wikipedia
1552class exponnorm_gen(rv_continuous):
1553 r"""An exponentially modified Normal continuous random variable.
1555 %(before_notes)s
1557 Notes
1558 -----
1559 The probability density function for `exponnorm` is:
1561 .. math::
1563 f(x, K) = \frac{1}{2K} \exp\left(\frac{1}{2 K^2} - x / K \right)
1564 \text{erfc}\left(-\frac{x - 1/K}{\sqrt{2}}\right)
1566 where :math:`x` is a real number and :math:`K > 0`.
1568 It can be thought of as the sum of a standard normal random variable
1569 and an independent exponentially distributed random variable with rate
1570 ``1/K``.
1572 %(after_notes)s
1574 An alternative parameterization of this distribution (for example, in
1575 `Wikipedia <https://en.wikipedia.org/wiki/Exponentially_modified_Gaussian_distribution>`_)
1576 involves three parameters, :math:`\mu`, :math:`\lambda` and
1577 :math:`\sigma`.
1578 In the present parameterization this corresponds to having ``loc`` and
1579 ``scale`` equal to :math:`\mu` and :math:`\sigma`, respectively, and
1580 shape parameter :math:`K = 1/(\sigma\lambda)`.
1582 .. versionadded:: 0.16.0
1584 %(example)s
1586 """
1587 def _rvs(self, K, size=None, random_state=None):
1588 expval = random_state.standard_exponential(size) * K
1589 gval = random_state.standard_normal(size)
1590 return expval + gval
1592 def _pdf(self, x, K):
1593 # exponnorm.pdf(x, K) =
1594 # 1/(2*K) exp(1/(2 * K**2)) exp(-x / K) * erfc-(x - 1/K) / sqrt(2))
1595 invK = 1.0 / K
1596 exparg = 0.5 * invK**2 - invK * x
1597 # Avoid overflows; setting np.exp(exparg) to the max float works
1598 # all right here
1599 expval = _lazywhere(exparg < _LOGXMAX, (exparg,), np.exp, _XMAX)
1600 return 0.5 * invK * (expval * sc.erfc(-(x - invK) / np.sqrt(2)))
1602 def _logpdf(self, x, K):
1603 invK = 1.0 / K
1604 exparg = 0.5 * invK**2 - invK * x
1605 return exparg + np.log(0.5 * invK * sc.erfc(-(x - invK) / np.sqrt(2)))
1607 def _cdf(self, x, K):
1608 invK = 1.0 / K
1609 expval = invK * (0.5 * invK - x)
1610 return _norm_cdf(x) - np.exp(expval) * _norm_cdf(x - invK)
1612 def _sf(self, x, K):
1613 invK = 1.0 / K
1614 expval = invK * (0.5 * invK - x)
1615 return _norm_cdf(-x) + np.exp(expval) * _norm_cdf(x - invK)
1617 def _stats(self, K):
1618 K2 = K * K
1619 opK2 = 1.0 + K2
1620 skw = 2 * K**3 * opK2**(-1.5)
1621 krt = 6.0 * K2 * K2 * opK2**(-2)
1622 return K, opK2, skw, krt
1625exponnorm = exponnorm_gen(name='exponnorm')
1628class exponweib_gen(rv_continuous):
1629 r"""An exponentiated Weibull continuous random variable.
1631 %(before_notes)s
1633 See Also
1634 --------
1635 weibull_min, numpy.random.RandomState.weibull
1637 Notes
1638 -----
1639 The probability density function for `exponweib` is:
1641 .. math::
1643 f(x, a, c) = a c [1-\exp(-x^c)]^{a-1} \exp(-x^c) x^{c-1}
1645 and its cumulative distribution function is:
1647 .. math::
1649 F(x, a, c) = [1-\exp(-x^c)]^a
1651 for :math:`x > 0`, :math:`a > 0`, :math:`c > 0`.
1653 `exponweib` takes :math:`a` and :math:`c` as shape parameters:
1655 * :math:`a` is the exponentiation parameter,
1656 with the special case :math:`a=1` corresponding to the
1657 (non-exponentiated) Weibull distribution `weibull_min`.
1658 * :math:`c` is the shape parameter of the non-exponentiated Weibull law.
1660 %(after_notes)s
1662 References
1663 ----------
1664 https://en.wikipedia.org/wiki/Exponentiated_Weibull_distribution
1666 %(example)s
1668 """
1669 def _pdf(self, x, a, c):
1670 # exponweib.pdf(x, a, c) =
1671 # a * c * (1-exp(-x**c))**(a-1) * exp(-x**c)*x**(c-1)
1672 return np.exp(self._logpdf(x, a, c))
1674 def _logpdf(self, x, a, c):
1675 negxc = -x**c
1676 exm1c = -sc.expm1(negxc)
1677 logp = (np.log(a) + np.log(c) + sc.xlogy(a - 1.0, exm1c) +
1678 negxc + sc.xlogy(c - 1.0, x))
1679 return logp
1681 def _cdf(self, x, a, c):
1682 exm1c = -sc.expm1(-x**c)
1683 return exm1c**a
1685 def _ppf(self, q, a, c):
1686 return (-sc.log1p(-q**(1.0/a)))**np.asarray(1.0/c)
1689exponweib = exponweib_gen(a=0.0, name='exponweib')
1692class exponpow_gen(rv_continuous):
1693 r"""An exponential power continuous random variable.
1695 %(before_notes)s
1697 Notes
1698 -----
1699 The probability density function for `exponpow` is:
1701 .. math::
1703 f(x, b) = b x^{b-1} \exp(1 + x^b - \exp(x^b))
1705 for :math:`x \ge 0`, :math:`b > 0`. Note that this is a different
1706 distribution from the exponential power distribution that is also known
1707 under the names "generalized normal" or "generalized Gaussian".
1709 `exponpow` takes ``b`` as a shape parameter for :math:`b`.
1711 %(after_notes)s
1713 References
1714 ----------
1715 http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Exponentialpower.pdf
1717 %(example)s
1719 """
1720 def _pdf(self, x, b):
1721 # exponpow.pdf(x, b) = b * x**(b-1) * exp(1 + x**b - exp(x**b))
1722 return np.exp(self._logpdf(x, b))
1724 def _logpdf(self, x, b):
1725 xb = x**b
1726 f = 1 + np.log(b) + sc.xlogy(b - 1.0, x) + xb - np.exp(xb)
1727 return f
1729 def _cdf(self, x, b):
1730 return -sc.expm1(-sc.expm1(x**b))
1732 def _sf(self, x, b):
1733 return np.exp(-sc.expm1(x**b))
1735 def _isf(self, x, b):
1736 return (sc.log1p(-np.log(x)))**(1./b)
1738 def _ppf(self, q, b):
1739 return pow(sc.log1p(-sc.log1p(-q)), 1.0/b)
1742exponpow = exponpow_gen(a=0.0, name='exponpow')
1745class fatiguelife_gen(rv_continuous):
1746 r"""A fatigue-life (Birnbaum-Saunders) continuous random variable.
1748 %(before_notes)s
1750 Notes
1751 -----
1752 The probability density function for `fatiguelife` is:
1754 .. math::
1756 f(x, c) = \frac{x+1}{2c\sqrt{2\pi x^3}} \exp(-\frac{(x-1)^2}{2x c^2})
1758 for :math:`x >= 0` and :math:`c > 0`.
1760 `fatiguelife` takes ``c`` as a shape parameter for :math:`c`.
1762 %(after_notes)s
1764 References
1765 ----------
1766 .. [1] "Birnbaum-Saunders distribution",
1767 https://en.wikipedia.org/wiki/Birnbaum-Saunders_distribution
1769 %(example)s
1771 """
1772 _support_mask = rv_continuous._open_support_mask
1774 def _rvs(self, c, size=None, random_state=None):
1775 z = random_state.standard_normal(size)
1776 x = 0.5*c*z
1777 x2 = x*x
1778 t = 1.0 + 2*x2 + 2*x*np.sqrt(1 + x2)
1779 return t
1781 def _pdf(self, x, c):
1782 # fatiguelife.pdf(x, c) =
1783 # (x+1) / (2*c*sqrt(2*pi*x**3)) * exp(-(x-1)**2/(2*x*c**2))
1784 return np.exp(self._logpdf(x, c))
1786 def _logpdf(self, x, c):
1787 return (np.log(x+1) - (x-1)**2 / (2.0*x*c**2) - np.log(2*c) -
1788 0.5*(np.log(2*np.pi) + 3*np.log(x)))
1790 def _cdf(self, x, c):
1791 return _norm_cdf(1.0 / c * (np.sqrt(x) - 1.0/np.sqrt(x)))
1793 def _ppf(self, q, c):
1794 tmp = c*sc.ndtri(q)
1795 return 0.25 * (tmp + np.sqrt(tmp**2 + 4))**2
1797 def _stats(self, c):
1798 # NB: the formula for kurtosis in wikipedia seems to have an error:
1799 # it's 40, not 41. At least it disagrees with the one from Wolfram
1800 # Alpha. And the latter one, below, passes the tests, while the wiki
1801 # one doesn't So far I didn't have the guts to actually check the
1802 # coefficients from the expressions for the raw moments.
1803 c2 = c*c
1804 mu = c2 / 2.0 + 1.0
1805 den = 5.0 * c2 + 4.0
1806 mu2 = c2*den / 4.0
1807 g1 = 4 * c * (11*c2 + 6.0) / np.power(den, 1.5)
1808 g2 = 6 * c2 * (93*c2 + 40.0) / den**2.0
1809 return mu, mu2, g1, g2
1812fatiguelife = fatiguelife_gen(a=0.0, name='fatiguelife')
1815class foldcauchy_gen(rv_continuous):
1816 r"""A folded Cauchy continuous random variable.
1818 %(before_notes)s
1820 Notes
1821 -----
1822 The probability density function for `foldcauchy` is:
1824 .. math::
1826 f(x, c) = \frac{1}{\pi (1+(x-c)^2)} + \frac{1}{\pi (1+(x+c)^2)}
1828 for :math:`x \ge 0`.
1830 `foldcauchy` takes ``c`` as a shape parameter for :math:`c`.
1832 %(example)s
1834 """
1835 def _rvs(self, c, size=None, random_state=None):
1836 return abs(cauchy.rvs(loc=c, size=size,
1837 random_state=random_state))
1839 def _pdf(self, x, c):
1840 # foldcauchy.pdf(x, c) = 1/(pi*(1+(x-c)**2)) + 1/(pi*(1+(x+c)**2))
1841 return 1.0/np.pi*(1.0/(1+(x-c)**2) + 1.0/(1+(x+c)**2))
1843 def _cdf(self, x, c):
1844 return 1.0/np.pi*(np.arctan(x-c) + np.arctan(x+c))
1846 def _stats(self, c):
1847 return np.inf, np.inf, np.nan, np.nan
1850foldcauchy = foldcauchy_gen(a=0.0, name='foldcauchy')
1853class f_gen(rv_continuous):
1854 r"""An F continuous random variable.
1856 %(before_notes)s
1858 Notes
1859 -----
1860 The probability density function for `f` is:
1862 .. math::
1864 f(x, df_1, df_2) = \frac{df_2^{df_2/2} df_1^{df_1/2} x^{df_1 / 2-1}}
1865 {(df_2+df_1 x)^{(df_1+df_2)/2}
1866 B(df_1/2, df_2/2)}
1868 for :math:`x > 0`.
1870 `f` takes ``dfn`` and ``dfd`` as shape parameters.
1872 %(after_notes)s
1874 %(example)s
1876 """
1877 def _rvs(self, dfn, dfd, size=None, random_state=None):
1878 return random_state.f(dfn, dfd, size)
1880 def _pdf(self, x, dfn, dfd):
1881 # df2**(df2/2) * df1**(df1/2) * x**(df1/2-1)
1882 # F.pdf(x, df1, df2) = --------------------------------------------
1883 # (df2+df1*x)**((df1+df2)/2) * B(df1/2, df2/2)
1884 return np.exp(self._logpdf(x, dfn, dfd))
1886 def _logpdf(self, x, dfn, dfd):
1887 n = 1.0 * dfn
1888 m = 1.0 * dfd
1889 lPx = m/2 * np.log(m) + n/2 * np.log(n) + sc.xlogy(n/2 - 1, x)
1890 lPx -= ((n+m)/2) * np.log(m + n*x) + sc.betaln(n/2, m/2)
1891 return lPx
1893 def _cdf(self, x, dfn, dfd):
1894 return sc.fdtr(dfn, dfd, x)
1896 def _sf(self, x, dfn, dfd):
1897 return sc.fdtrc(dfn, dfd, x)
1899 def _ppf(self, q, dfn, dfd):
1900 return sc.fdtri(dfn, dfd, q)
1902 def _stats(self, dfn, dfd):
1903 v1, v2 = 1. * dfn, 1. * dfd
1904 v2_2, v2_4, v2_6, v2_8 = v2 - 2., v2 - 4., v2 - 6., v2 - 8.
1906 mu = _lazywhere(
1907 v2 > 2, (v2, v2_2),
1908 lambda v2, v2_2: v2 / v2_2,
1909 np.inf)
1911 mu2 = _lazywhere(
1912 v2 > 4, (v1, v2, v2_2, v2_4),
1913 lambda v1, v2, v2_2, v2_4:
1914 2 * v2 * v2 * (v1 + v2_2) / (v1 * v2_2**2 * v2_4),
1915 np.inf)
1917 g1 = _lazywhere(
1918 v2 > 6, (v1, v2_2, v2_4, v2_6),
1919 lambda v1, v2_2, v2_4, v2_6:
1920 (2 * v1 + v2_2) / v2_6 * np.sqrt(v2_4 / (v1 * (v1 + v2_2))),
1921 np.nan)
1922 g1 *= np.sqrt(8.)
1924 g2 = _lazywhere(
1925 v2 > 8, (g1, v2_6, v2_8),
1926 lambda g1, v2_6, v2_8: (8 + g1 * g1 * v2_6) / v2_8,
1927 np.nan)
1928 g2 *= 3. / 2.
1930 return mu, mu2, g1, g2
1933f = f_gen(a=0.0, name='f')
1936## Folded Normal
1937## abs(Z) where (Z is normal with mu=L and std=S so that c=abs(L)/S)
1938##
1939## note: regress docs have scale parameter correct, but first parameter
1940## he gives is a shape parameter A = c * scale
1942## Half-normal is folded normal with shape-parameter c=0.
1944class foldnorm_gen(rv_continuous):
1945 r"""A folded normal continuous random variable.
1947 %(before_notes)s
1949 Notes
1950 -----
1951 The probability density function for `foldnorm` is:
1953 .. math::
1955 f(x, c) = \sqrt{2/\pi} cosh(c x) \exp(-\frac{x^2+c^2}{2})
1957 for :math:`c \ge 0`.
1959 `foldnorm` takes ``c`` as a shape parameter for :math:`c`.
1961 %(after_notes)s
1963 %(example)s
1965 """
1966 def _argcheck(self, c):
1967 return c >= 0
1969 def _rvs(self, c, size=None, random_state=None):
1970 return abs(random_state.standard_normal(size) + c)
1972 def _pdf(self, x, c):
1973 # foldnormal.pdf(x, c) = sqrt(2/pi) * cosh(c*x) * exp(-(x**2+c**2)/2)
1974 return _norm_pdf(x + c) + _norm_pdf(x-c)
1976 def _cdf(self, x, c):
1977 return _norm_cdf(x-c) + _norm_cdf(x+c) - 1.0
1979 def _stats(self, c):
1980 # Regina C. Elandt, Technometrics 3, 551 (1961)
1981 # https://www.jstor.org/stable/1266561
1982 #
1983 c2 = c*c
1984 expfac = np.exp(-0.5*c2) / np.sqrt(2.*np.pi)
1986 mu = 2.*expfac + c * sc.erf(c/np.sqrt(2))
1987 mu2 = c2 + 1 - mu*mu
1989 g1 = 2. * (mu*mu*mu - c2*mu - expfac)
1990 g1 /= np.power(mu2, 1.5)
1992 g2 = c2 * (c2 + 6.) + 3 + 8.*expfac*mu
1993 g2 += (2. * (c2 - 3.) - 3. * mu**2) * mu**2
1994 g2 = g2 / mu2**2.0 - 3.
1996 return mu, mu2, g1, g2
1999foldnorm = foldnorm_gen(a=0.0, name='foldnorm')
2002class weibull_min_gen(rv_continuous):
2003 r"""Weibull minimum continuous random variable.
2005 The Weibull Minimum Extreme Value distribution, from extreme value theory
2006 (Fisher-Gnedenko theorem), is also often simply called the Weibull
2007 distribution. It arises as the limiting distribution of the rescaled
2008 minimum of iid random variables.
2010 %(before_notes)s
2012 See Also
2013 --------
2014 weibull_max, numpy.random.RandomState.weibull, exponweib
2016 Notes
2017 -----
2018 The probability density function for `weibull_min` is:
2020 .. math::
2022 f(x, c) = c x^{c-1} \exp(-x^c)
2024 for :math:`x > 0`, :math:`c > 0`.
2026 `weibull_min` takes ``c`` as a shape parameter for :math:`c`.
2027 (named :math:`k` in Wikipedia article and :math:`a` in
2028 ``numpy.random.weibull``). Special shape values are :math:`c=1` and
2029 :math:`c=2` where Weibull distribution reduces to the `expon` and
2030 `rayleigh` distributions respectively.
2032 %(after_notes)s
2034 References
2035 ----------
2036 https://en.wikipedia.org/wiki/Weibull_distribution
2038 https://en.wikipedia.org/wiki/Fisher-Tippett-Gnedenko_theorem
2040 %(example)s
2042 """
2044 def _pdf(self, x, c):
2045 # frechet_r.pdf(x, c) = c * x**(c-1) * exp(-x**c)
2046 return c*pow(x, c-1)*np.exp(-pow(x, c))
2048 def _logpdf(self, x, c):
2049 return np.log(c) + sc.xlogy(c - 1, x) - pow(x, c)
2051 def _cdf(self, x, c):
2052 return -sc.expm1(-pow(x, c))
2054 def _sf(self, x, c):
2055 return np.exp(-pow(x, c))
2057 def _logsf(self, x, c):
2058 return -pow(x, c)
2060 def _ppf(self, q, c):
2061 return pow(-sc.log1p(-q), 1.0/c)
2063 def _munp(self, n, c):
2064 return sc.gamma(1.0+n*1.0/c)
2066 def _entropy(self, c):
2067 return -_EULER / c - np.log(c) + _EULER + 1
2070weibull_min = weibull_min_gen(a=0.0, name='weibull_min')
2073class weibull_max_gen(rv_continuous):
2074 r"""Weibull maximum continuous random variable.
2076 The Weibull Maximum Extreme Value distribution, from extreme value theory
2077 (Fisher-Gnedenko theorem), is the limiting distribution of rescaled
2078 maximum of iid random variables. This is the distribution of -X
2079 if X is from the `weibull_min` function.
2081 %(before_notes)s
2083 See Also
2084 --------
2085 weibull_min
2087 Notes
2088 -----
2089 The probability density function for `weibull_max` is:
2091 .. math::
2093 f(x, c) = c (-x)^{c-1} \exp(-(-x)^c)
2095 for :math:`x < 0`, :math:`c > 0`.
2097 `weibull_max` takes ``c`` as a shape parameter for :math:`c`.
2099 %(after_notes)s
2101 References
2102 ----------
2103 https://en.wikipedia.org/wiki/Weibull_distribution
2105 https://en.wikipedia.org/wiki/Fisher-Tippett-Gnedenko_theorem
2107 %(example)s
2109 """
2110 def _pdf(self, x, c):
2111 # frechet_l.pdf(x, c) = c * (-x)**(c-1) * exp(-(-x)**c)
2112 return c*pow(-x, c-1)*np.exp(-pow(-x, c))
2114 def _logpdf(self, x, c):
2115 return np.log(c) + sc.xlogy(c-1, -x) - pow(-x, c)
2117 def _cdf(self, x, c):
2118 return np.exp(-pow(-x, c))
2120 def _logcdf(self, x, c):
2121 return -pow(-x, c)
2123 def _sf(self, x, c):
2124 return -sc.expm1(-pow(-x, c))
2126 def _ppf(self, q, c):
2127 return -pow(-np.log(q), 1.0/c)
2129 def _munp(self, n, c):
2130 val = sc.gamma(1.0+n*1.0/c)
2131 if int(n) % 2:
2132 sgn = -1
2133 else:
2134 sgn = 1
2135 return sgn * val
2137 def _entropy(self, c):
2138 return -_EULER / c - np.log(c) + _EULER + 1
2141weibull_max = weibull_max_gen(b=0.0, name='weibull_max')
2143# Public methods to be deprecated in frechet_r and frechet_l:
2144# ['__call__', 'cdf', 'entropy', 'expect', 'fit', 'fit_loc_scale', 'freeze',
2145# 'interval', 'isf', 'logcdf', 'logpdf', 'logsf', 'mean', 'median', 'moment',
2146# 'nnlf', 'pdf', 'ppf', 'rvs', 'sf', 'stats', 'std', 'var']
2148_frechet_r_deprec_msg = """\
2149The distribution `frechet_r` is a synonym for `weibull_min`; this historical
2150usage is deprecated because of possible confusion with the (quite different)
2151Frechet distribution. To preserve the existing behavior of the program, use
2152`scipy.stats.weibull_min`. For the Frechet distribution (i.e. the Type II
2153extreme value distribution), use `scipy.stats.invweibull`."""
2156class frechet_r_gen(weibull_min_gen):
2157 """A Frechet right (or Weibull minimum) continuous random variable.
2159 %(before_notes)s
2161 See Also
2162 --------
2163 weibull_min : The same distribution as `frechet_r`.
2165 Notes
2166 -----
2167 %(after_notes)s
2169 %(example)s
2170 """
2172 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2173 def __call__(self, *args, **kwargs):
2174 return weibull_min_gen.__call__(self, *args, **kwargs)
2176 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2177 def cdf(self, *args, **kwargs):
2178 return weibull_min_gen.cdf(self, *args, **kwargs)
2180 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2181 def entropy(self, *args, **kwargs):
2182 return weibull_min_gen.entropy(self, *args, **kwargs)
2184 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2185 def expect(self, *args, **kwargs):
2186 return weibull_min_gen.expect(self, *args, **kwargs)
2188 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2189 def fit(self, *args, **kwargs):
2190 return weibull_min_gen.fit(self, *args, **kwargs)
2192 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2193 def fit_loc_scale(self, *args, **kwargs):
2194 return weibull_min_gen.fit_loc_scale(self, *args, **kwargs)
2196 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2197 def freeze(self, *args, **kwargs):
2198 return weibull_min_gen.freeze(self, *args, **kwargs)
2200 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2201 def interval(self, *args, **kwargs):
2202 return weibull_min_gen.interval(self, *args, **kwargs)
2204 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2205 def isf(self, *args, **kwargs):
2206 return weibull_min_gen.isf(self, *args, **kwargs)
2208 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2209 def logcdf(self, *args, **kwargs):
2210 return weibull_min_gen.logcdf(self, *args, **kwargs)
2212 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2213 def logpdf(self, *args, **kwargs):
2214 return weibull_min_gen.logpdf(self, *args, **kwargs)
2216 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2217 def logsf(self, *args, **kwargs):
2218 return weibull_min_gen.logsf(self, *args, **kwargs)
2220 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2221 def mean(self, *args, **kwargs):
2222 return weibull_min_gen.mean(self, *args, **kwargs)
2224 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2225 def median(self, *args, **kwargs):
2226 return weibull_min_gen.median(self, *args, **kwargs)
2228 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2229 def moment(self, *args, **kwargs):
2230 return weibull_min_gen.moment(self, *args, **kwargs)
2232 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2233 def nnlf(self, *args, **kwargs):
2234 return weibull_min_gen.nnlf(self, *args, **kwargs)
2236 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2237 def pdf(self, *args, **kwargs):
2238 return weibull_min_gen.pdf(self, *args, **kwargs)
2240 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2241 def ppf(self, *args, **kwargs):
2242 return weibull_min_gen.ppf(self, *args, **kwargs)
2244 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2245 def rvs(self, *args, **kwargs):
2246 return weibull_min_gen.rvs(self, *args, **kwargs)
2248 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2249 def sf(self, *args, **kwargs):
2250 return weibull_min_gen.sf(self, *args, **kwargs)
2252 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2253 def stats(self, *args, **kwargs):
2254 return weibull_min_gen.stats(self, *args, **kwargs)
2256 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2257 def std(self, *args, **kwargs):
2258 return weibull_min_gen.std(self, *args, **kwargs)
2260 @np.deprecate(old_name='frechet_r', message=_frechet_r_deprec_msg)
2261 def var(self, *args, **kwargs):
2262 return weibull_min_gen.var(self, *args, **kwargs)
2265frechet_r = frechet_r_gen(a=0.0, name='frechet_r')
2268_frechet_l_deprec_msg = """\
2269The distribution `frechet_l` is a synonym for `weibull_max`; this historical
2270usage is deprecated because of possible confusion with the (quite different)
2271Frechet distribution. To preserve the existing behavior of the program, use
2272`scipy.stats.weibull_max`. For the Frechet distribution (i.e. the Type II
2273extreme value distribution), use `scipy.stats.invweibull`."""
2276class frechet_l_gen(weibull_max_gen):
2277 """A Frechet left (or Weibull maximum) continuous random variable.
2279 %(before_notes)s
2281 See Also
2282 --------
2283 weibull_max : The same distribution as `frechet_l`.
2285 Notes
2286 -----
2287 %(after_notes)s
2289 %(example)s
2290 """
2292 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2293 def __call__(self, *args, **kwargs):
2294 return weibull_max_gen.__call__(self, *args, **kwargs)
2296 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2297 def cdf(self, *args, **kwargs):
2298 return weibull_max_gen.cdf(self, *args, **kwargs)
2300 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2301 def entropy(self, *args, **kwargs):
2302 return weibull_max_gen.entropy(self, *args, **kwargs)
2304 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2305 def expect(self, *args, **kwargs):
2306 return weibull_max_gen.expect(self, *args, **kwargs)
2308 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2309 def fit(self, *args, **kwargs):
2310 return weibull_max_gen.fit(self, *args, **kwargs)
2312 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2313 def fit_loc_scale(self, *args, **kwargs):
2314 return weibull_max_gen.fit_loc_scale(self, *args, **kwargs)
2316 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2317 def freeze(self, *args, **kwargs):
2318 return weibull_max_gen.freeze(self, *args, **kwargs)
2320 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2321 def interval(self, *args, **kwargs):
2322 return weibull_max_gen.interval(self, *args, **kwargs)
2324 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2325 def isf(self, *args, **kwargs):
2326 return weibull_max_gen.isf(self, *args, **kwargs)
2328 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2329 def logcdf(self, *args, **kwargs):
2330 return weibull_max_gen.logcdf(self, *args, **kwargs)
2332 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2333 def logpdf(self, *args, **kwargs):
2334 return weibull_max_gen.logpdf(self, *args, **kwargs)
2336 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2337 def logsf(self, *args, **kwargs):
2338 return weibull_max_gen.logsf(self, *args, **kwargs)
2340 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2341 def mean(self, *args, **kwargs):
2342 return weibull_max_gen.mean(self, *args, **kwargs)
2344 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2345 def median(self, *args, **kwargs):
2346 return weibull_max_gen.median(self, *args, **kwargs)
2348 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2349 def moment(self, *args, **kwargs):
2350 return weibull_max_gen.moment(self, *args, **kwargs)
2352 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2353 def nnlf(self, *args, **kwargs):
2354 return weibull_max_gen.nnlf(self, *args, **kwargs)
2356 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2357 def pdf(self, *args, **kwargs):
2358 return weibull_max_gen.pdf(self, *args, **kwargs)
2360 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2361 def ppf(self, *args, **kwargs):
2362 return weibull_max_gen.ppf(self, *args, **kwargs)
2364 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2365 def rvs(self, *args, **kwargs):
2366 return weibull_max_gen.rvs(self, *args, **kwargs)
2368 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2369 def sf(self, *args, **kwargs):
2370 return weibull_max_gen.sf(self, *args, **kwargs)
2372 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2373 def stats(self, *args, **kwargs):
2374 return weibull_max_gen.stats(self, *args, **kwargs)
2376 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2377 def std(self, *args, **kwargs):
2378 return weibull_max_gen.std(self, *args, **kwargs)
2380 @np.deprecate(old_name='frechet_l', message=_frechet_l_deprec_msg)
2381 def var(self, *args, **kwargs):
2382 return weibull_max_gen.var(self, *args, **kwargs)
2385frechet_l = frechet_l_gen(b=0.0, name='frechet_l')
2388class genlogistic_gen(rv_continuous):
2389 r"""A generalized logistic continuous random variable.
2391 %(before_notes)s
2393 Notes
2394 -----
2395 The probability density function for `genlogistic` is:
2397 .. math::
2399 f(x, c) = c \frac{\exp(-x)}
2400 {(1 + \exp(-x))^{c+1}}
2402 for :math:`x >= 0`, :math:`c > 0`.
2404 `genlogistic` takes ``c`` as a shape parameter for :math:`c`.
2406 %(after_notes)s
2408 %(example)s
2410 """
2411 def _pdf(self, x, c):
2412 # genlogistic.pdf(x, c) = c * exp(-x) / (1 + exp(-x))**(c+1)
2413 return np.exp(self._logpdf(x, c))
2415 def _logpdf(self, x, c):
2416 return np.log(c) - x - (c+1.0)*sc.log1p(np.exp(-x))
2418 def _cdf(self, x, c):
2419 Cx = (1+np.exp(-x))**(-c)
2420 return Cx
2422 def _ppf(self, q, c):
2423 vals = -np.log(pow(q, -1.0/c)-1)
2424 return vals
2426 def _stats(self, c):
2427 mu = _EULER + sc.psi(c)
2428 mu2 = np.pi*np.pi/6.0 + sc.zeta(2, c)
2429 g1 = -2*sc.zeta(3, c) + 2*_ZETA3
2430 g1 /= np.power(mu2, 1.5)
2431 g2 = np.pi**4/15.0 + 6*sc.zeta(4, c)
2432 g2 /= mu2**2.0
2433 return mu, mu2, g1, g2
2436genlogistic = genlogistic_gen(name='genlogistic')
2439class genpareto_gen(rv_continuous):
2440 r"""A generalized Pareto continuous random variable.
2442 %(before_notes)s
2444 Notes
2445 -----
2446 The probability density function for `genpareto` is:
2448 .. math::
2450 f(x, c) = (1 + c x)^{-1 - 1/c}
2452 defined for :math:`x \ge 0` if :math:`c \ge 0`, and for
2453 :math:`0 \le x \le -1/c` if :math:`c < 0`.
2455 `genpareto` takes ``c`` as a shape parameter for :math:`c`.
2457 For :math:`c=0`, `genpareto` reduces to the exponential
2458 distribution, `expon`:
2460 .. math::
2462 f(x, 0) = \exp(-x)
2464 For :math:`c=-1`, `genpareto` is uniform on ``[0, 1]``:
2466 .. math::
2468 f(x, -1) = 1
2470 %(after_notes)s
2472 %(example)s
2474 """
2475 def _argcheck(self, c):
2476 return np.isfinite(c)
2478 def _get_support(self, c):
2479 c = np.asarray(c)
2480 b = _lazywhere(c < 0, (c,),
2481 lambda c: -1. / c,
2482 np.inf)
2483 a = np.where(c >= 0, self.a, self.a)
2484 return a, b
2486 def _pdf(self, x, c):
2487 # genpareto.pdf(x, c) = (1 + c * x)**(-1 - 1/c)
2488 return np.exp(self._logpdf(x, c))
2490 def _logpdf(self, x, c):
2491 return _lazywhere((x == x) & (c != 0), (x, c),
2492 lambda x, c: -sc.xlog1py(c + 1., c*x) / c,
2493 -x)
2495 def _cdf(self, x, c):
2496 return -sc.inv_boxcox1p(-x, -c)
2498 def _sf(self, x, c):
2499 return sc.inv_boxcox(-x, -c)
2501 def _logsf(self, x, c):
2502 return _lazywhere((x == x) & (c != 0), (x, c),
2503 lambda x, c: -sc.log1p(c*x) / c,
2504 -x)
2506 def _ppf(self, q, c):
2507 return -sc.boxcox1p(-q, -c)
2509 def _isf(self, q, c):
2510 return -sc.boxcox(q, -c)
2512 def _stats(self, c, moments='mv'):
2513 if 'm' not in moments:
2514 m = None
2515 else:
2516 m = _lazywhere(c < 1, (c,),
2517 lambda xi: 1/(1 - xi),
2518 np.inf)
2519 if 'v' not in moments:
2520 v = None
2521 else:
2522 v = _lazywhere(c < 1/2, (c,),
2523 lambda xi: 1 / (1 - xi)**2 / (1 - 2*xi),
2524 np.nan)
2525 if 's' not in moments:
2526 s = None
2527 else:
2528 s = _lazywhere(c < 1/3, (c,),
2529 lambda xi: 2 * (1 + xi) * np.sqrt(1 - 2*xi) /
2530 (1 - 3*xi),
2531 np.nan)
2532 if 'k' not in moments:
2533 k = None
2534 else:
2535 k = _lazywhere(c < 1/4, (c,),
2536 lambda xi: 3 * (1 - 2*xi) * (2*xi**2 + xi + 3) /
2537 (1 - 3*xi) / (1 - 4*xi) - 3,
2538 np.nan)
2539 return m, v, s, k
2541 def _munp(self, n, c):
2542 def __munp(n, c):
2543 val = 0.0
2544 k = np.arange(0, n + 1)
2545 for ki, cnk in zip(k, sc.comb(n, k)):
2546 val = val + cnk * (-1) ** ki / (1.0 - c * ki)
2547 return np.where(c * n < 1, val * (-1.0 / c) ** n, np.inf)
2548 return _lazywhere(c != 0, (c,),
2549 lambda c: __munp(n, c),
2550 sc.gamma(n + 1))
2552 def _entropy(self, c):
2553 return 1. + c
2556genpareto = genpareto_gen(a=0.0, name='genpareto')
2559class genexpon_gen(rv_continuous):
2560 r"""A generalized exponential continuous random variable.
2562 %(before_notes)s
2564 Notes
2565 -----
2566 The probability density function for `genexpon` is:
2568 .. math::
2570 f(x, a, b, c) = (a + b (1 - \exp(-c x)))
2571 \exp(-a x - b x + \frac{b}{c} (1-\exp(-c x)))
2573 for :math:`x \ge 0`, :math:`a, b, c > 0`.
2575 `genexpon` takes :math:`a`, :math:`b` and :math:`c` as shape parameters.
2577 %(after_notes)s
2579 References
2580 ----------
2581 H.K. Ryu, "An Extension of Marshall and Olkin's Bivariate Exponential
2582 Distribution", Journal of the American Statistical Association, 1993.
2584 N. Balakrishnan, "The Exponential Distribution: Theory, Methods and
2585 Applications", Asit P. Basu.
2587 %(example)s
2589 """
2590 def _pdf(self, x, a, b, c):
2591 # genexpon.pdf(x, a, b, c) = (a + b * (1 - exp(-c*x))) * \
2592 # exp(-a*x - b*x + b/c * (1-exp(-c*x)))
2593 return (a + b*(-sc.expm1(-c*x)))*np.exp((-a-b)*x +
2594 b*(-sc.expm1(-c*x))/c)
2596 def _cdf(self, x, a, b, c):
2597 return -sc.expm1((-a-b)*x + b*(-sc.expm1(-c*x))/c)
2599 def _logpdf(self, x, a, b, c):
2600 return np.log(a+b*(-sc.expm1(-c*x))) + (-a-b)*x+b*(-sc.expm1(-c*x))/c
2603genexpon = genexpon_gen(a=0.0, name='genexpon')
2606class genextreme_gen(rv_continuous):
2607 r"""A generalized extreme value continuous random variable.
2609 %(before_notes)s
2611 See Also
2612 --------
2613 gumbel_r
2615 Notes
2616 -----
2617 For :math:`c=0`, `genextreme` is equal to `gumbel_r`.
2618 The probability density function for `genextreme` is:
2620 .. math::
2622 f(x, c) = \begin{cases}
2623 \exp(-\exp(-x)) \exp(-x) &\text{for } c = 0\\
2624 \exp(-(1-c x)^{1/c}) (1-c x)^{1/c-1} &\text{for }
2625 x \le 1/c, c > 0
2626 \end{cases}
2629 Note that several sources and software packages use the opposite
2630 convention for the sign of the shape parameter :math:`c`.
2632 `genextreme` takes ``c`` as a shape parameter for :math:`c`.
2634 %(after_notes)s
2636 %(example)s
2638 """
2639 def _argcheck(self, c):
2640 return np.where(abs(c) == np.inf, 0, 1)
2642 def _get_support(self, c):
2643 _b = np.where(c > 0, 1.0 / np.maximum(c, _XMIN), np.inf)
2644 _a = np.where(c < 0, 1.0 / np.minimum(c, -_XMIN), -np.inf)
2645 return _a, _b
2647 def _loglogcdf(self, x, c):
2648 return _lazywhere((x == x) & (c != 0), (x, c),
2649 lambda x, c: sc.log1p(-c*x)/c, -x)
2651 def _pdf(self, x, c):
2652 # genextreme.pdf(x, c) =
2653 # exp(-exp(-x))*exp(-x), for c==0
2654 # exp(-(1-c*x)**(1/c))*(1-c*x)**(1/c-1), for x \le 1/c, c > 0
2655 return np.exp(self._logpdf(x, c))
2657 def _logpdf(self, x, c):
2658 cx = _lazywhere((x == x) & (c != 0), (x, c), lambda x, c: c*x, 0.0)
2659 logex2 = sc.log1p(-cx)
2660 logpex2 = self._loglogcdf(x, c)
2661 pex2 = np.exp(logpex2)
2662 # Handle special cases
2663 np.putmask(logpex2, (c == 0) & (x == -np.inf), 0.0)
2664 logpdf = np.where((cx == 1) | (cx == -np.inf),
2665 -np.inf,
2666 -pex2+logpex2-logex2)
2667 np.putmask(logpdf, (c == 1) & (x == 1), 0.0)
2668 return logpdf
2670 def _logcdf(self, x, c):
2671 return -np.exp(self._loglogcdf(x, c))
2673 def _cdf(self, x, c):
2674 return np.exp(self._logcdf(x, c))
2676 def _sf(self, x, c):
2677 return -sc.expm1(self._logcdf(x, c))
2679 def _ppf(self, q, c):
2680 x = -np.log(-np.log(q))
2681 return _lazywhere((x == x) & (c != 0), (x, c),
2682 lambda x, c: -sc.expm1(-c * x) / c, x)
2684 def _isf(self, q, c):
2685 x = -np.log(-sc.log1p(-q))
2686 return _lazywhere((x == x) & (c != 0), (x, c),
2687 lambda x, c: -sc.expm1(-c * x) / c, x)
2689 def _stats(self, c):
2690 g = lambda n: sc.gamma(n*c + 1)
2691 g1 = g(1)
2692 g2 = g(2)
2693 g3 = g(3)
2694 g4 = g(4)
2695 g2mg12 = np.where(abs(c) < 1e-7, (c*np.pi)**2.0/6.0, g2-g1**2.0)
2696 gam2k = np.where(abs(c) < 1e-7, np.pi**2.0/6.0,
2697 sc.expm1(sc.gammaln(2.0*c+1.0)-2*sc.gammaln(c + 1.0))/c**2.0)
2698 eps = 1e-14
2699 gamk = np.where(abs(c) < eps, -_EULER, sc.expm1(sc.gammaln(c + 1))/c)
2701 m = np.where(c < -1.0, np.nan, -gamk)
2702 v = np.where(c < -0.5, np.nan, g1**2.0*gam2k)
2704 # skewness
2705 sk1 = _lazywhere(c >= -1./3,
2706 (c, g1, g2, g3, g2mg12),
2707 lambda c, g1, g2, g3, g2gm12:
2708 np.sign(c)*(-g3 + (g2 + 2*g2mg12)*g1)/g2mg12**1.5,
2709 fillvalue=np.nan)
2710 sk = np.where(abs(c) <= eps**0.29, 12*np.sqrt(6)*_ZETA3/np.pi**3, sk1)
2712 # kurtosis
2713 ku1 = _lazywhere(c >= -1./4,
2714 (g1, g2, g3, g4, g2mg12),
2715 lambda g1, g2, g3, g4, g2mg12:
2716 (g4 + (-4*g3 + 3*(g2 + g2mg12)*g1)*g1)/g2mg12**2,
2717 fillvalue=np.nan)
2718 ku = np.where(abs(c) <= (eps)**0.23, 12.0/5.0, ku1-3.0)
2719 return m, v, sk, ku
2721 def _fitstart(self, data):
2722 # This is better than the default shape of (1,).
2723 g = _skew(data)
2724 if g < 0:
2725 a = 0.5
2726 else:
2727 a = -0.5
2728 return super(genextreme_gen, self)._fitstart(data, args=(a,))
2730 def _munp(self, n, c):
2731 k = np.arange(0, n+1)
2732 vals = 1.0/c**n * np.sum(
2733 sc.comb(n, k) * (-1)**k * sc.gamma(c*k + 1),
2734 axis=0)
2735 return np.where(c*n > -1, vals, np.inf)
2737 def _entropy(self, c):
2738 return _EULER*(1 - c) + 1
2741genextreme = genextreme_gen(name='genextreme')
2744def _digammainv(y):
2745 # Inverse of the digamma function (real positive arguments only).
2746 # This function is used in the `fit` method of `gamma_gen`.
2747 # The function uses either optimize.fsolve or optimize.newton
2748 # to solve `sc.digamma(x) - y = 0`. There is probably room for
2749 # improvement, but currently it works over a wide range of y:
2750 # >>> y = 64*np.random.randn(1000000)
2751 # >>> y.min(), y.max()
2752 # (-311.43592651416662, 351.77388222276869)
2753 # x = [_digammainv(t) for t in y]
2754 # np.abs(sc.digamma(x) - y).max()
2755 # 1.1368683772161603e-13
2756 #
2757 _em = 0.5772156649015328606065120
2758 func = lambda x: sc.digamma(x) - y
2759 if y > -0.125:
2760 x0 = np.exp(y) + 0.5
2761 if y < 10:
2762 # Some experimentation shows that newton reliably converges
2763 # must faster than fsolve in this y range. For larger y,
2764 # newton sometimes fails to converge.
2765 value = optimize.newton(func, x0, tol=1e-10)
2766 return value
2767 elif y > -3:
2768 x0 = np.exp(y/2.332) + 0.08661
2769 else:
2770 x0 = 1.0 / (-y - _em)
2772 value, info, ier, mesg = optimize.fsolve(func, x0, xtol=1e-11,
2773 full_output=True)
2774 if ier != 1:
2775 raise RuntimeError("_digammainv: fsolve failed, y = %r" % y)
2777 return value[0]
2780## Gamma (Use MATLAB and MATHEMATICA (b=theta=scale, a=alpha=shape) definition)
2782## gamma(a, loc, scale) with a an integer is the Erlang distribution
2783## gamma(1, loc, scale) is the Exponential distribution
2784## gamma(df/2, 0, 2) is the chi2 distribution with df degrees of freedom.
2786class gamma_gen(rv_continuous):
2787 r"""A gamma continuous random variable.
2789 %(before_notes)s
2791 See Also
2792 --------
2793 erlang, expon
2795 Notes
2796 -----
2797 The probability density function for `gamma` is:
2799 .. math::
2801 f(x, a) = \frac{x^{a-1} \exp(-x)}{\Gamma(a)}
2803 for :math:`x \ge 0`, :math:`a > 0`. Here :math:`\Gamma(a)` refers to the
2804 gamma function.
2806 `gamma` takes ``a`` as a shape parameter for :math:`a`.
2808 When :math:`a` is an integer, `gamma` reduces to the Erlang
2809 distribution, and when :math:`a=1` to the exponential distribution.
2811 %(after_notes)s
2813 %(example)s
2815 """
2816 def _rvs(self, a, size=None, random_state=None):
2817 return random_state.standard_gamma(a, size)
2819 def _pdf(self, x, a):
2820 # gamma.pdf(x, a) = x**(a-1) * exp(-x) / gamma(a)
2821 return np.exp(self._logpdf(x, a))
2823 def _logpdf(self, x, a):
2824 return sc.xlogy(a-1.0, x) - x - sc.gammaln(a)
2826 def _cdf(self, x, a):
2827 return sc.gammainc(a, x)
2829 def _sf(self, x, a):
2830 return sc.gammaincc(a, x)
2832 def _ppf(self, q, a):
2833 return sc.gammaincinv(a, q)
2835 def _stats(self, a):
2836 return a, a, 2.0/np.sqrt(a), 6.0/a
2838 def _entropy(self, a):
2839 return sc.psi(a)*(1-a) + a + sc.gammaln(a)
2841 def _fitstart(self, data):
2842 # The skewness of the gamma distribution is `4 / np.sqrt(a)`.
2843 # We invert that to estimate the shape `a` using the skewness
2844 # of the data. The formula is regularized with 1e-8 in the
2845 # denominator to allow for degenerate data where the skewness
2846 # is close to 0.
2847 a = 4 / (1e-8 + _skew(data)**2)
2848 return super(gamma_gen, self)._fitstart(data, args=(a,))
2850 @extend_notes_in_docstring(rv_continuous, notes="""\
2851 When the location is fixed by using the argument `floc`, this
2852 function uses explicit formulas or solves a simpler numerical
2853 problem than the full ML optimization problem. So in that case,
2854 the `optimizer`, `loc` and `scale` arguments are ignored.\n\n""")
2855 def fit(self, data, *args, **kwds):
2856 floc = kwds.get('floc', None)
2858 if floc is None:
2859 # loc is not fixed. Use the default fit method.
2860 return super(gamma_gen, self).fit(data, *args, **kwds)
2862 # We already have this value, so just pop it from kwds.
2863 kwds.pop('floc', None)
2865 f0 = _get_fixed_fit_value(kwds, ['f0', 'fa', 'fix_a'])
2866 fscale = kwds.pop('fscale', None)
2868 _remove_optimizer_parameters(kwds)
2870 # Special case: loc is fixed.
2872 if f0 is not None and fscale is not None:
2873 # This check is for consistency with `rv_continuous.fit`.
2874 # Without this check, this function would just return the
2875 # parameters that were given.
2876 raise ValueError("All parameters fixed. There is nothing to "
2877 "optimize.")
2879 # Fixed location is handled by shifting the data.
2880 data = np.asarray(data)
2882 if not np.isfinite(data).all():
2883 raise RuntimeError("The data contains non-finite values.")
2885 if np.any(data <= floc):
2886 raise FitDataError("gamma", lower=floc, upper=np.inf)
2888 if floc != 0:
2889 # Don't do the subtraction in-place, because `data` might be a
2890 # view of the input array.
2891 data = data - floc
2892 xbar = data.mean()
2894 # Three cases to handle:
2895 # * shape and scale both free
2896 # * shape fixed, scale free
2897 # * shape free, scale fixed
2899 if fscale is None:
2900 # scale is free
2901 if f0 is not None:
2902 # shape is fixed
2903 a = f0
2904 else:
2905 # shape and scale are both free.
2906 # The MLE for the shape parameter `a` is the solution to:
2907 # np.log(a) - sc.digamma(a) - np.log(xbar) +
2908 # np.log(data).mean() = 0
2909 s = np.log(xbar) - np.log(data).mean()
2910 func = lambda a: np.log(a) - sc.digamma(a) - s
2911 aest = (3-s + np.sqrt((s-3)**2 + 24*s)) / (12*s)
2912 xa = aest*(1-0.4)
2913 xb = aest*(1+0.4)
2914 a = optimize.brentq(func, xa, xb, disp=0)
2916 # The MLE for the scale parameter is just the data mean
2917 # divided by the shape parameter.
2918 scale = xbar / a
2919 else:
2920 # scale is fixed, shape is free
2921 # The MLE for the shape parameter `a` is the solution to:
2922 # sc.digamma(a) - np.log(data).mean() + np.log(fscale) = 0
2923 c = np.log(data).mean() - np.log(fscale)
2924 a = _digammainv(c)
2925 scale = fscale
2927 return a, floc, scale
2930gamma = gamma_gen(a=0.0, name='gamma')
2933class erlang_gen(gamma_gen):
2934 """An Erlang continuous random variable.
2936 %(before_notes)s
2938 See Also
2939 --------
2940 gamma
2942 Notes
2943 -----
2944 The Erlang distribution is a special case of the Gamma distribution, with
2945 the shape parameter `a` an integer. Note that this restriction is not
2946 enforced by `erlang`. It will, however, generate a warning the first time
2947 a non-integer value is used for the shape parameter.
2949 Refer to `gamma` for examples.
2951 """
2953 def _argcheck(self, a):
2954 allint = np.all(np.floor(a) == a)
2955 if not allint:
2956 # An Erlang distribution shouldn't really have a non-integer
2957 # shape parameter, so warn the user.
2958 warnings.warn(
2959 'The shape parameter of the erlang distribution '
2960 'has been given a non-integer value %r.' % (a,),
2961 RuntimeWarning)
2962 return a > 0
2964 def _fitstart(self, data):
2965 # Override gamma_gen_fitstart so that an integer initial value is
2966 # used. (Also regularize the division, to avoid issues when
2967 # _skew(data) is 0 or close to 0.)
2968 a = int(4.0 / (1e-8 + _skew(data)**2))
2969 return super(gamma_gen, self)._fitstart(data, args=(a,))
2971 # Trivial override of the fit method, so we can monkey-patch its
2972 # docstring.
2973 def fit(self, data, *args, **kwds):
2974 return super(erlang_gen, self).fit(data, *args, **kwds)
2976 if fit.__doc__:
2977 fit.__doc__ = (rv_continuous.fit.__doc__ +
2978 """
2979 Notes
2980 -----
2981 The Erlang distribution is generally defined to have integer values
2982 for the shape parameter. This is not enforced by the `erlang` class.
2983 When fitting the distribution, it will generally return a non-integer
2984 value for the shape parameter. By using the keyword argument
2985 `f0=<integer>`, the fit method can be constrained to fit the data to
2986 a specific integer shape parameter.
2987 """)
2990erlang = erlang_gen(a=0.0, name='erlang')
2993class gengamma_gen(rv_continuous):
2994 r"""A generalized gamma continuous random variable.
2996 %(before_notes)s
2998 Notes
2999 -----
3000 The probability density function for `gengamma` is:
3002 .. math::
3004 f(x, a, c) = \frac{|c| x^{c a-1} \exp(-x^c)}{\Gamma(a)}
3006 for :math:`x \ge 0`, :math:`a > 0`, and :math:`c \ne 0`.
3007 :math:`\Gamma` is the gamma function (`scipy.special.gamma`).
3009 `gengamma` takes :math:`a` and :math:`c` as shape parameters.
3011 %(after_notes)s
3013 %(example)s
3015 """
3016 def _argcheck(self, a, c):
3017 return (a > 0) & (c != 0)
3019 def _pdf(self, x, a, c):
3020 # gengamma.pdf(x, a, c) = abs(c) * x**(c*a-1) * exp(-x**c) / gamma(a)
3021 return np.exp(self._logpdf(x, a, c))
3023 def _logpdf(self, x, a, c):
3024 return np.log(abs(c)) + sc.xlogy(c*a - 1, x) - x**c - sc.gammaln(a)
3026 def _cdf(self, x, a, c):
3027 xc = x**c
3028 val1 = sc.gammainc(a, xc)
3029 val2 = sc.gammaincc(a, xc)
3030 return np.where(c > 0, val1, val2)
3032 def _sf(self, x, a, c):
3033 xc = x**c
3034 val1 = sc.gammainc(a, xc)
3035 val2 = sc.gammaincc(a, xc)
3036 return np.where(c > 0, val2, val1)
3038 def _ppf(self, q, a, c):
3039 val1 = sc.gammaincinv(a, q)
3040 val2 = sc.gammainccinv(a, q)
3041 return np.where(c > 0, val1, val2)**(1.0/c)
3043 def _isf(self, q, a, c):
3044 val1 = sc.gammaincinv(a, q)
3045 val2 = sc.gammainccinv(a, q)
3046 return np.where(c > 0, val2, val1)**(1.0/c)
3048 def _munp(self, n, a, c):
3049 # Pochhammer symbol: sc.pocha,n) = gamma(a+n)/gamma(a)
3050 return sc.poch(a, n*1.0/c)
3052 def _entropy(self, a, c):
3053 val = sc.psi(a)
3054 return a*(1-val) + 1.0/c*val + sc.gammaln(a) - np.log(abs(c))
3057gengamma = gengamma_gen(a=0.0, name='gengamma')
3060class genhalflogistic_gen(rv_continuous):
3061 r"""A generalized half-logistic continuous random variable.
3063 %(before_notes)s
3065 Notes
3066 -----
3067 The probability density function for `genhalflogistic` is:
3069 .. math::
3071 f(x, c) = \frac{2 (1 - c x)^{1/(c-1)}}{[1 + (1 - c x)^{1/c}]^2}
3073 for :math:`0 \le x \le 1/c`, and :math:`c > 0`.
3075 `genhalflogistic` takes ``c`` as a shape parameter for :math:`c`.
3077 %(after_notes)s
3079 %(example)s
3081 """
3082 def _argcheck(self, c):
3083 return c > 0
3085 def _get_support(self, c):
3086 return self.a, 1.0/c
3088 def _pdf(self, x, c):
3089 # genhalflogistic.pdf(x, c) =
3090 # 2 * (1-c*x)**(1/c-1) / (1+(1-c*x)**(1/c))**2
3091 limit = 1.0/c
3092 tmp = np.asarray(1-c*x)
3093 tmp0 = tmp**(limit-1)
3094 tmp2 = tmp0*tmp
3095 return 2*tmp0 / (1+tmp2)**2
3097 def _cdf(self, x, c):
3098 limit = 1.0/c
3099 tmp = np.asarray(1-c*x)
3100 tmp2 = tmp**(limit)
3101 return (1.0-tmp2) / (1+tmp2)
3103 def _ppf(self, q, c):
3104 return 1.0/c*(1-((1.0-q)/(1.0+q))**c)
3106 def _entropy(self, c):
3107 return 2 - (2*c+1)*np.log(2)
3110genhalflogistic = genhalflogistic_gen(a=0.0, name='genhalflogistic')
3113class gompertz_gen(rv_continuous):
3114 r"""A Gompertz (or truncated Gumbel) continuous random variable.
3116 %(before_notes)s
3118 Notes
3119 -----
3120 The probability density function for `gompertz` is:
3122 .. math::
3124 f(x, c) = c \exp(x) \exp(-c (e^x-1))
3126 for :math:`x \ge 0`, :math:`c > 0`.
3128 `gompertz` takes ``c`` as a shape parameter for :math:`c`.
3130 %(after_notes)s
3132 %(example)s
3134 """
3135 def _pdf(self, x, c):
3136 # gompertz.pdf(x, c) = c * exp(x) * exp(-c*(exp(x)-1))
3137 return np.exp(self._logpdf(x, c))
3139 def _logpdf(self, x, c):
3140 return np.log(c) + x - c * sc.expm1(x)
3142 def _cdf(self, x, c):
3143 return -sc.expm1(-c * sc.expm1(x))
3145 def _ppf(self, q, c):
3146 return sc.log1p(-1.0 / c * sc.log1p(-q))
3148 def _entropy(self, c):
3149 return 1.0 - np.log(c) - np.exp(c)*sc.expn(1, c)
3152gompertz = gompertz_gen(a=0.0, name='gompertz')
3155class gumbel_r_gen(rv_continuous):
3156 r"""A right-skewed Gumbel continuous random variable.
3158 %(before_notes)s
3160 See Also
3161 --------
3162 gumbel_l, gompertz, genextreme
3164 Notes
3165 -----
3166 The probability density function for `gumbel_r` is:
3168 .. math::
3170 f(x) = \exp(-(x + e^{-x}))
3172 The Gumbel distribution is sometimes referred to as a type I Fisher-Tippett
3173 distribution. It is also related to the extreme value distribution,
3174 log-Weibull and Gompertz distributions.
3176 %(after_notes)s
3178 %(example)s
3180 """
3181 def _pdf(self, x):
3182 # gumbel_r.pdf(x) = exp(-(x + exp(-x)))
3183 return np.exp(self._logpdf(x))
3185 def _logpdf(self, x):
3186 return -x - np.exp(-x)
3188 def _cdf(self, x):
3189 return np.exp(-np.exp(-x))
3191 def _logcdf(self, x):
3192 return -np.exp(-x)
3194 def _ppf(self, q):
3195 return -np.log(-np.log(q))
3197 def _stats(self):
3198 return _EULER, np.pi*np.pi/6.0, 12*np.sqrt(6)/np.pi**3 * _ZETA3, 12.0/5
3200 def _entropy(self):
3201 # https://en.wikipedia.org/wiki/Gumbel_distribution
3202 return _EULER + 1.
3205gumbel_r = gumbel_r_gen(name='gumbel_r')
3208class gumbel_l_gen(rv_continuous):
3209 r"""A left-skewed Gumbel continuous random variable.
3211 %(before_notes)s
3213 See Also
3214 --------
3215 gumbel_r, gompertz, genextreme
3217 Notes
3218 -----
3219 The probability density function for `gumbel_l` is:
3221 .. math::
3223 f(x) = \exp(x - e^x)
3225 The Gumbel distribution is sometimes referred to as a type I Fisher-Tippett
3226 distribution. It is also related to the extreme value distribution,
3227 log-Weibull and Gompertz distributions.
3229 %(after_notes)s
3231 %(example)s
3233 """
3234 def _pdf(self, x):
3235 # gumbel_l.pdf(x) = exp(x - exp(x))
3236 return np.exp(self._logpdf(x))
3238 def _logpdf(self, x):
3239 return x - np.exp(x)
3241 def _cdf(self, x):
3242 return -sc.expm1(-np.exp(x))
3244 def _ppf(self, q):
3245 return np.log(-sc.log1p(-q))
3247 def _logsf(self, x):
3248 return -np.exp(x)
3250 def _sf(self, x):
3251 return np.exp(-np.exp(x))
3253 def _isf(self, x):
3254 return np.log(-np.log(x))
3256 def _stats(self):
3257 return -_EULER, np.pi*np.pi/6.0, \
3258 -12*np.sqrt(6)/np.pi**3 * _ZETA3, 12.0/5
3260 def _entropy(self):
3261 return _EULER + 1.
3264gumbel_l = gumbel_l_gen(name='gumbel_l')
3267class halfcauchy_gen(rv_continuous):
3268 r"""A Half-Cauchy continuous random variable.
3270 %(before_notes)s
3272 Notes
3273 -----
3274 The probability density function for `halfcauchy` is:
3276 .. math::
3278 f(x) = \frac{2}{\pi (1 + x^2)}
3280 for :math:`x \ge 0`.
3282 %(after_notes)s
3284 %(example)s
3286 """
3287 def _pdf(self, x):
3288 # halfcauchy.pdf(x) = 2 / (pi * (1 + x**2))
3289 return 2.0/np.pi/(1.0+x*x)
3291 def _logpdf(self, x):
3292 return np.log(2.0/np.pi) - sc.log1p(x*x)
3294 def _cdf(self, x):
3295 return 2.0/np.pi*np.arctan(x)
3297 def _ppf(self, q):
3298 return np.tan(np.pi/2*q)
3300 def _stats(self):
3301 return np.inf, np.inf, np.nan, np.nan
3303 def _entropy(self):
3304 return np.log(2*np.pi)
3307halfcauchy = halfcauchy_gen(a=0.0, name='halfcauchy')
3310class halflogistic_gen(rv_continuous):
3311 r"""A half-logistic continuous random variable.
3313 %(before_notes)s
3315 Notes
3316 -----
3317 The probability density function for `halflogistic` is:
3319 .. math::
3321 f(x) = \frac{ 2 e^{-x} }{ (1+e^{-x})^2 }
3322 = \frac{1}{2} \text{sech}(x/2)^2
3324 for :math:`x \ge 0`.
3326 %(after_notes)s
3328 %(example)s
3330 """
3331 def _pdf(self, x):
3332 # halflogistic.pdf(x) = 2 * exp(-x) / (1+exp(-x))**2
3333 # = 1/2 * sech(x/2)**2
3334 return np.exp(self._logpdf(x))
3336 def _logpdf(self, x):
3337 return np.log(2) - x - 2. * sc.log1p(np.exp(-x))
3339 def _cdf(self, x):
3340 return np.tanh(x/2.0)
3342 def _ppf(self, q):
3343 return 2*np.arctanh(q)
3345 def _munp(self, n):
3346 if n == 1:
3347 return 2*np.log(2)
3348 if n == 2:
3349 return np.pi*np.pi/3.0
3350 if n == 3:
3351 return 9*_ZETA3
3352 if n == 4:
3353 return 7*np.pi**4 / 15.0
3354 return 2*(1-pow(2.0, 1-n))*sc.gamma(n+1)*sc.zeta(n, 1)
3356 def _entropy(self):
3357 return 2-np.log(2)
3360halflogistic = halflogistic_gen(a=0.0, name='halflogistic')
3363class halfnorm_gen(rv_continuous):
3364 r"""A half-normal continuous random variable.
3366 %(before_notes)s
3368 Notes
3369 -----
3370 The probability density function for `halfnorm` is:
3372 .. math::
3374 f(x) = \sqrt{2/\pi} \exp(-x^2 / 2)
3376 for :math:`x >= 0`.
3378 `halfnorm` is a special case of `chi` with ``df=1``.
3380 %(after_notes)s
3382 %(example)s
3384 """
3385 def _rvs(self, size=None, random_state=None):
3386 return abs(random_state.standard_normal(size=size))
3388 def _pdf(self, x):
3389 # halfnorm.pdf(x) = sqrt(2/pi) * exp(-x**2/2)
3390 return np.sqrt(2.0/np.pi)*np.exp(-x*x/2.0)
3392 def _logpdf(self, x):
3393 return 0.5 * np.log(2.0/np.pi) - x*x/2.0
3395 def _cdf(self, x):
3396 return _norm_cdf(x)*2-1.0
3398 def _ppf(self, q):
3399 return sc.ndtri((1+q)/2.0)
3401 def _stats(self):
3402 return (np.sqrt(2.0/np.pi),
3403 1-2.0/np.pi,
3404 np.sqrt(2)*(4-np.pi)/(np.pi-2)**1.5,
3405 8*(np.pi-3)/(np.pi-2)**2)
3407 def _entropy(self):
3408 return 0.5*np.log(np.pi/2.0)+0.5
3411halfnorm = halfnorm_gen(a=0.0, name='halfnorm')
3414class hypsecant_gen(rv_continuous):
3415 r"""A hyperbolic secant continuous random variable.
3417 %(before_notes)s
3419 Notes
3420 -----
3421 The probability density function for `hypsecant` is:
3423 .. math::
3425 f(x) = \frac{1}{\pi} \text{sech}(x)
3427 for a real number :math:`x`.
3429 %(after_notes)s
3431 %(example)s
3433 """
3434 def _pdf(self, x):
3435 # hypsecant.pdf(x) = 1/pi * sech(x)
3436 return 1.0/(np.pi*np.cosh(x))
3438 def _cdf(self, x):
3439 return 2.0/np.pi*np.arctan(np.exp(x))
3441 def _ppf(self, q):
3442 return np.log(np.tan(np.pi*q/2.0))
3444 def _stats(self):
3445 return 0, np.pi*np.pi/4, 0, 2
3447 def _entropy(self):
3448 return np.log(2*np.pi)
3451hypsecant = hypsecant_gen(name='hypsecant')
3454class gausshyper_gen(rv_continuous):
3455 r"""A Gauss hypergeometric continuous random variable.
3457 %(before_notes)s
3459 Notes
3460 -----
3461 The probability density function for `gausshyper` is:
3463 .. math::
3465 f(x, a, b, c, z) = C x^{a-1} (1-x)^{b-1} (1+zx)^{-c}
3467 for :math:`0 \le x \le 1`, :math:`a > 0`, :math:`b > 0`, and
3468 :math:`C = \frac{1}{B(a, b) F[2, 1](c, a; a+b; -z)}`.
3469 :math:`F[2, 1]` is the Gauss hypergeometric function
3470 `scipy.special.hyp2f1`.
3472 `gausshyper` takes :math:`a`, :math:`b`, :math:`c` and :math:`z` as shape
3473 parameters.
3475 %(after_notes)s
3477 %(example)s
3479 """
3480 def _argcheck(self, a, b, c, z):
3481 return (a > 0) & (b > 0) & (c == c) & (z == z)
3483 def _pdf(self, x, a, b, c, z):
3484 # gausshyper.pdf(x, a, b, c, z) =
3485 # C * x**(a-1) * (1-x)**(b-1) * (1+z*x)**(-c)
3486 Cinv = sc.gamma(a)*sc.gamma(b)/sc.gamma(a+b)*sc.hyp2f1(c, a, a+b, -z)
3487 return 1.0/Cinv * x**(a-1.0) * (1.0-x)**(b-1.0) / (1.0+z*x)**c
3489 def _munp(self, n, a, b, c, z):
3490 fac = sc.beta(n+a, b) / sc.beta(a, b)
3491 num = sc.hyp2f1(c, a+n, a+b+n, -z)
3492 den = sc.hyp2f1(c, a, a+b, -z)
3493 return fac*num / den
3496gausshyper = gausshyper_gen(a=0.0, b=1.0, name='gausshyper')
3499class invgamma_gen(rv_continuous):
3500 r"""An inverted gamma continuous random variable.
3502 %(before_notes)s
3504 Notes
3505 -----
3506 The probability density function for `invgamma` is:
3508 .. math::
3510 f(x, a) = \frac{x^{-a-1}}{\Gamma(a)} \exp(-\frac{1}{x})
3512 for :math:`x >= 0`, :math:`a > 0`. :math:`\Gamma` is the gamma function
3513 (`scipy.special.gamma`).
3515 `invgamma` takes ``a`` as a shape parameter for :math:`a`.
3517 `invgamma` is a special case of `gengamma` with ``c=-1``.
3519 %(after_notes)s
3521 %(example)s
3523 """
3524 _support_mask = rv_continuous._open_support_mask
3526 def _pdf(self, x, a):
3527 # invgamma.pdf(x, a) = x**(-a-1) / gamma(a) * exp(-1/x)
3528 return np.exp(self._logpdf(x, a))
3530 def _logpdf(self, x, a):
3531 return -(a+1) * np.log(x) - sc.gammaln(a) - 1.0/x
3533 def _cdf(self, x, a):
3534 return sc.gammaincc(a, 1.0 / x)
3536 def _ppf(self, q, a):
3537 return 1.0 / sc.gammainccinv(a, q)
3539 def _sf(self, x, a):
3540 return sc.gammainc(a, 1.0 / x)
3542 def _isf(self, q, a):
3543 return 1.0 / sc.gammaincinv(a, q)
3545 def _stats(self, a, moments='mvsk'):
3546 m1 = _lazywhere(a > 1, (a,), lambda x: 1. / (x - 1.), np.inf)
3547 m2 = _lazywhere(a > 2, (a,), lambda x: 1. / (x - 1.)**2 / (x - 2.),
3548 np.inf)
3550 g1, g2 = None, None
3551 if 's' in moments:
3552 g1 = _lazywhere(
3553 a > 3, (a,),
3554 lambda x: 4. * np.sqrt(x - 2.) / (x - 3.), np.nan)
3555 if 'k' in moments:
3556 g2 = _lazywhere(
3557 a > 4, (a,),
3558 lambda x: 6. * (5. * x - 11.) / (x - 3.) / (x - 4.), np.nan)
3559 return m1, m2, g1, g2
3561 def _entropy(self, a):
3562 return a - (a+1.0) * sc.psi(a) + sc.gammaln(a)
3565invgamma = invgamma_gen(a=0.0, name='invgamma')
3568# scale is gamma from DATAPLOT and B from Regress
3569class invgauss_gen(rv_continuous):
3570 r"""An inverse Gaussian continuous random variable.
3572 %(before_notes)s
3574 Notes
3575 -----
3576 The probability density function for `invgauss` is:
3578 .. math::
3580 f(x, \mu) = \frac{1}{\sqrt{2 \pi x^3}}
3581 \exp(-\frac{(x-\mu)^2}{2 x \mu^2})
3583 for :math:`x >= 0` and :math:`\mu > 0`.
3585 `invgauss` takes ``mu`` as a shape parameter for :math:`\mu`.
3587 %(after_notes)s
3589 When :math:`\mu` is too small, evaluating the cumulative distribution
3590 function will be inaccurate due to ``cdf(mu -> 0) = inf * 0``.
3591 NaNs are returned for :math:`\mu \le 0.0028`.
3593 %(example)s
3595 """
3596 _support_mask = rv_continuous._open_support_mask
3598 def _rvs(self, mu, size=None, random_state=None):
3599 return random_state.wald(mu, 1.0, size=size)
3601 def _pdf(self, x, mu):
3602 # invgauss.pdf(x, mu) =
3603 # 1 / sqrt(2*pi*x**3) * exp(-(x-mu)**2/(2*x*mu**2))
3604 return 1.0/np.sqrt(2*np.pi*x**3.0)*np.exp(-1.0/(2*x)*((x-mu)/mu)**2)
3606 def _logpdf(self, x, mu):
3607 return -0.5*np.log(2*np.pi) - 1.5*np.log(x) - ((x-mu)/mu)**2/(2*x)
3609 def _cdf(self, x, mu):
3610 fac = np.sqrt(1.0/x)
3611 # Numerical accuracy for small `mu` is bad. See #869.
3612 C1 = _norm_cdf(fac*(x-mu)/mu)
3613 C1 += np.exp(1.0/mu) * _norm_cdf(-fac*(x+mu)/mu) * np.exp(1.0/mu)
3614 return C1
3616 def _stats(self, mu):
3617 return mu, mu**3.0, 3*np.sqrt(mu), 15*mu
3620invgauss = invgauss_gen(a=0.0, name='invgauss')
3623class geninvgauss_gen(rv_continuous):
3624 r"""A Generalized Inverse Gaussian continuous random variable.
3626 %(before_notes)s
3628 Notes
3629 -----
3630 The probability density function for `geninvgauss` is:
3632 .. math::
3634 f(x, p, b) = x^{p-1} \exp(-b (x + 1/x) / 2) / (2 K_p(b))
3636 where `x > 0`, and the parameters `p, b` satisfy `b > 0` ([1]_).
3637 :math:`K_p` is the modified Bessel function of second kind of order `p`
3638 (`scipy.special.kv`).
3640 %(after_notes)s
3642 The inverse Gaussian distribution `stats.invgauss(mu)` is a special case of
3643 `geninvgauss` with `p = -1/2`, `b = 1 / mu` and `scale = mu`.
3645 Generating random variates is challenging for this distribution. The
3646 implementation is based on [2]_.
3648 References
3649 ----------
3650 .. [1] O. Barndorff-Nielsen, P. Blaesild, C. Halgreen, "First hitting time
3651 models for the generalized inverse gaussian distribution",
3652 Stochastic Processes and their Applications 7, pp. 49--54, 1978.
3654 .. [2] W. Hoermann and J. Leydold, "Generating generalized inverse Gaussian
3655 random variates", Statistics and Computing, 24(4), p. 547--557, 2014.
3657 %(example)s
3659 """
3660 def _argcheck(self, p, b):
3661 return (p == p) & (b > 0)
3663 def _logpdf(self, x, p, b):
3664 # kve instead of kv works better for large values of b
3665 # warn if kve produces infinite values and replace by nan
3666 # otherwise c = -inf and the results are often incorrect
3667 @np.vectorize
3668 def logpdf_single(x, p, b):
3669 return _stats.geninvgauss_logpdf(x, p, b)
3671 z = logpdf_single(x, p, b)
3672 if np.isnan(z).any():
3673 msg = ("Infinite values encountered in scipy.special.kve(p, b). "
3674 "Values replaced by NaN to avoid incorrect results.")
3675 warnings.warn(msg, RuntimeWarning)
3676 return z
3678 def _pdf(self, x, p, b):
3679 # relying on logpdf avoids overflow of x**(p-1) for large x and p
3680 return np.exp(self._logpdf(x, p, b))
3682 def _cdf(self, x, *args):
3683 _a, _b = self._get_support(*args)
3685 @np.vectorize
3686 def _cdf_single(x, *args):
3687 p, b = args
3688 user_data = np.array([p, b], float).ctypes.data_as(ctypes.c_void_p)
3689 llc = LowLevelCallable.from_cython(_stats, '_geninvgauss_pdf', user_data)
3691 return integrate.quad(llc, _a, x)[0]
3693 return _cdf_single(x, *args)
3695 def _logquasipdf(self, x, p, b):
3696 # log of the quasi-density (w/o normalizing constant) used in _rvs
3697 return _lazywhere(x > 0, (x, p, b),
3698 lambda x, p, b: (p - 1)*np.log(x) - b*(x + 1/x)/2,
3699 -np.inf)
3701 def _rvs(self, p, b, size=None, random_state=None):
3702 # if p and b are scalar, use _rvs_scalar, otherwise need to create
3703 # output by iterating over parameters
3704 if np.isscalar(p) and np.isscalar(b):
3705 out = self._rvs_scalar(p, b, size, random_state)
3706 elif p.size == 1 and b.size == 1:
3707 out = self._rvs_scalar(p.item(), b.item(), size, random_state)
3708 else:
3709 # When this method is called, size will be a (possibly empty)
3710 # tuple of integers. It will not be None; if `size=None` is passed
3711 # to `rvs()`, size will be the empty tuple ().
3713 p, b = np.broadcast_arrays(p, b)
3714 # p and b now have the same shape.
3716 # `shp` is the shape of the blocks of random variates that are
3717 # generated for each combination of parameters associated with
3718 # broadcasting p and b.
3719 # bc is a tuple the same lenth as size. The values
3720 # in bc are bools. If bc[j] is True, it means that
3721 # entire axis is filled in for a given combination of the
3722 # broadcast arguments.
3723 shp, bc = _check_shape(p.shape, size)
3725 # `numsamples` is the total number of variates to be generated
3726 # for each combination of the input arguments.
3727 numsamples = int(np.prod(shp))
3729 # `out` is the array to be returned. It is filled in in the
3730 # loop below.
3731 out = np.empty(size)
3733 it = np.nditer([p, b],
3734 flags=['multi_index'],
3735 op_flags=[['readonly'], ['readonly']])
3736 while not it.finished:
3737 # Convert the iterator's multi_index into an index into the
3738 # `out` array where the call to _rvs_scalar() will be stored.
3739 # Where bc is True, we use a full slice; otherwise we use the
3740 # index value from it.multi_index. len(it.multi_index) might
3741 # be less than len(bc), and in that case we want to align these
3742 # two sequences to the right, so the loop variable j runs from
3743 # -len(size) to 0. This doesn't cause an IndexError, as
3744 # bc[j] will be True in those cases where it.multi_index[j]
3745 # would cause an IndexError.
3746 idx = tuple((it.multi_index[j] if not bc[j] else slice(None))
3747 for j in range(-len(size), 0))
3748 out[idx] = self._rvs_scalar(it[0], it[1], numsamples, random_state).reshape(shp)
3749 it.iternext()
3751 if size == ():
3752 out = out[()]
3753 return out
3755 def _rvs_scalar(self, p, b, numsamples, random_state):
3756 # following [2], the quasi-pdf is used instead of the pdf for the
3757 # generation of rvs
3758 invert_res = False
3759 if not(numsamples):
3760 numsamples = 1
3761 if p < 0:
3762 # note: if X is geninvgauss(p, b), then 1/X is geninvgauss(-p, b)
3763 p = -p
3764 invert_res = True
3765 m = self._mode(p, b)
3767 # determine method to be used following [2]
3768 ratio_unif = True
3769 if p >= 1 or b > 1:
3770 # ratio of uniforms with mode shift below
3771 mode_shift = True
3772 elif b >= min(0.5, 2 * np.sqrt(1 - p) / 3):
3773 # ratio of uniforms without mode shift below
3774 mode_shift = False
3775 else:
3776 # new algorithm in [2]
3777 ratio_unif = False
3779 # prepare sampling of rvs
3780 size1d = tuple(np.atleast_1d(numsamples))
3781 N = np.prod(size1d) # number of rvs needed, reshape upon return
3782 x = np.zeros(N)
3783 simulated = 0
3785 if ratio_unif:
3786 # use ratio of uniforms method
3787 if mode_shift:
3788 a2 = -2 * (p + 1) / b - m
3789 a1 = 2 * m * (p - 1) / b - 1
3790 # find roots of x**3 + a2*x**2 + a1*x + m (Cardano's formula)
3791 p1 = a1 - a2**2 / 3
3792 q1 = 2 * a2**3 / 27 - a2 * a1 / 3 + m
3793 phi = np.arccos(-q1 * np.sqrt(-27 / p1**3) / 2)
3794 s1 = -np.sqrt(-4 * p1 / 3)
3795 root1 = s1 * np.cos(phi / 3 + np.pi / 3) - a2 / 3
3796 root2 = -s1 * np.cos(phi / 3) - a2 / 3
3797 # root3 = s1 * np.cos(phi / 3 - np.pi / 3) - a2 / 3
3799 # if g is the quasipdf, rescale: g(x) / g(m) which we can write
3800 # as exp(log(g(x)) - log(g(m))). This is important
3801 # since for large values of p and b, g cannot be evaluated.
3802 # denote the rescaled quasipdf by h
3803 lm = self._logquasipdf(m, p, b)
3804 d1 = self._logquasipdf(root1, p, b) - lm
3805 d2 = self._logquasipdf(root2, p, b) - lm
3806 # compute the bounding rectangle w.r.t. h. Note that
3807 # np.exp(0.5*d1) = np.sqrt(g(root1)/g(m)) = np.sqrt(h(root1))
3808 vmin = (root1 - m) * np.exp(0.5 * d1)
3809 vmax = (root2 - m) * np.exp(0.5 * d2)
3810 umax = 1 # umax = sqrt(h(m)) = 1
3812 logqpdf = lambda x: self._logquasipdf(x, p, b) - lm
3813 c = m
3814 else:
3815 # ratio of uniforms without mode shift
3816 # compute np.sqrt(quasipdf(m))
3817 umax = np.exp(0.5*self._logquasipdf(m, p, b))
3818 xplus = ((1 + p) + np.sqrt((1 + p)**2 + b**2))/b
3819 vmin = 0
3820 # compute xplus * np.sqrt(quasipdf(xplus))
3821 vmax = xplus * np.exp(0.5 * self._logquasipdf(xplus, p, b))
3822 c = 0
3823 logqpdf = lambda x: self._logquasipdf(x, p, b)
3825 if vmin >= vmax:
3826 raise ValueError("vmin must be smaller than vmax.")
3827 if umax <= 0:
3828 raise ValueError("umax must be positive.")
3830 i = 1
3831 while simulated < N:
3832 k = N - simulated
3833 # simulate uniform rvs on [0, umax] and [vmin, vmax]
3834 u = umax * random_state.uniform(size=k)
3835 v = random_state.uniform(size=k)
3836 v = vmin + (vmax - vmin) * v
3837 rvs = v / u + c
3838 # rewrite acceptance condition u**2 <= pdf(rvs) by taking logs
3839 accept = (2*np.log(u) <= logqpdf(rvs))
3840 num_accept = np.sum(accept)
3841 if num_accept > 0:
3842 x[simulated:(simulated + num_accept)] = rvs[accept]
3843 simulated += num_accept
3845 if (simulated == 0) and (i*N >= 50000):
3846 msg = ("Not a single random variate could be generated "
3847 "in {} attempts. Sampling does not appear to "
3848 "work for the provided parameters.".format(i*N))
3849 raise RuntimeError(msg)
3850 i += 1
3851 else:
3852 # use new algorithm in [2]
3853 x0 = b / (1 - p)
3854 xs = np.max((x0, 2 / b))
3855 k1 = np.exp(self._logquasipdf(m, p, b))
3856 A1 = k1 * x0
3857 if x0 < 2 / b:
3858 k2 = np.exp(-b)
3859 if p > 0:
3860 A2 = k2 * ((2 / b)**p - x0**p) / p
3861 else:
3862 A2 = k2 * np.log(2 / b**2)
3863 else:
3864 k2, A2 = 0, 0
3865 k3 = xs**(p - 1)
3866 A3 = 2 * k3 * np.exp(-xs * b / 2) / b
3867 A = A1 + A2 + A3
3869 # [2]: rejection constant is < 2.73; so expected runtime is finite
3870 while simulated < N:
3871 k = N - simulated
3872 h, rvs = np.zeros(k), np.zeros(k)
3873 # simulate uniform rvs on [x1, x2] and [0, y2]
3874 u = random_state.uniform(size=k)
3875 v = A * random_state.uniform(size=k)
3876 cond1 = v <= A1
3877 cond2 = np.logical_not(cond1) & (v <= A1 + A2)
3878 cond3 = np.logical_not(cond1 | cond2)
3879 # subdomain (0, x0)
3880 rvs[cond1] = x0 * v[cond1] / A1
3881 h[cond1] = k1
3882 # subdomain (x0, 2 / b)
3883 if p > 0:
3884 rvs[cond2] = (x0**p + (v[cond2] - A1) * p / k2)**(1 / p)
3885 else:
3886 rvs[cond2] = b * np.exp((v[cond2] - A1) * np.exp(b))
3887 h[cond2] = k2 * rvs[cond2]**(p - 1)
3888 # subdomain (xs, infinity)
3889 z = np.exp(-xs * b / 2) - b * (v[cond3] - A1 - A2) / (2 * k3)
3890 rvs[cond3] = -2 / b * np.log(z)
3891 h[cond3] = k3 * np.exp(-rvs[cond3] * b / 2)
3892 # apply rejection method
3893 accept = (np.log(u * h) <= self._logquasipdf(rvs, p, b))
3894 num_accept = sum(accept)
3895 if num_accept > 0:
3896 x[simulated:(simulated + num_accept)] = rvs[accept]
3897 simulated += num_accept
3899 rvs = np.reshape(x, size1d)
3900 if invert_res:
3901 rvs = 1 / rvs
3902 return rvs
3904 def _mode(self, p, b):
3905 # distinguish cases to avoid catastrophic cancellation (see [2])
3906 if p < 1:
3907 return b / (np.sqrt((p - 1)**2 + b**2) + 1 - p)
3908 else:
3909 return (np.sqrt((1 - p)**2 + b**2) - (1 - p)) / b
3911 def _munp(self, n, p, b):
3912 num = sc.kve(p + n, b)
3913 denom = sc.kve(p, b)
3914 inf_vals = np.isinf(num) | np.isinf(denom)
3915 if inf_vals.any():
3916 msg = ("Infinite values encountered in the moment calculation "
3917 "involving scipy.special.kve. Values replaced by NaN to "
3918 "avoid incorrect results.")
3919 warnings.warn(msg, RuntimeWarning)
3920 m = np.full_like(num, np.nan, dtype=np.double)
3921 m[~inf_vals] = num[~inf_vals] / denom[~inf_vals]
3922 else:
3923 m = num / denom
3924 return m
3927geninvgauss = geninvgauss_gen(a=0.0, name="geninvgauss")
3930class norminvgauss_gen(rv_continuous):
3931 r"""A Normal Inverse Gaussian continuous random variable.
3933 %(before_notes)s
3935 Notes
3936 -----
3937 The probability density function for `norminvgauss` is:
3939 .. math::
3941 f(x, a, b) = \frac{a \, K_1(a \sqrt{1 + x^2})}{\pi \sqrt{1 + x^2}} \,
3942 \exp(\sqrt{a^2 - b^2} + b x)
3944 where :math:`x` is a real number, the parameter :math:`a` is the tail
3945 heaviness and :math:`b` is the asymmetry parameter satisfying
3946 :math:`a > 0` and :math:`|b| <= a`.
3947 :math:`K_1` is the modified Bessel function of second kind
3948 (`scipy.special.k1`).
3950 %(after_notes)s
3952 A normal inverse Gaussian random variable `Y` with parameters `a` and `b`
3953 can be expressed as a normal mean-variance mixture:
3954 `Y = b * V + sqrt(V) * X` where `X` is `norm(0,1)` and `V` is
3955 `invgauss(mu=1/sqrt(a**2 - b**2))`. This representation is used
3956 to generate random variates.
3958 References
3959 ----------
3960 O. Barndorff-Nielsen, "Hyperbolic Distributions and Distributions on
3961 Hyperbolae", Scandinavian Journal of Statistics, Vol. 5(3),
3962 pp. 151-157, 1978.
3964 O. Barndorff-Nielsen, "Normal Inverse Gaussian Distributions and Stochastic
3965 Volatility Modelling", Scandinavian Journal of Statistics, Vol. 24,
3966 pp. 1-13, 1997.
3968 %(example)s
3970 """
3971 _support_mask = rv_continuous._open_support_mask
3973 def _argcheck(self, a, b):
3974 return (a > 0) & (np.absolute(b) < a)
3976 def _pdf(self, x, a, b):
3977 gamma = np.sqrt(a**2 - b**2)
3978 fac1 = a / np.pi * np.exp(gamma)
3979 sq = np.hypot(1, x) # reduce overflows
3980 return fac1 * sc.k1e(a * sq) * np.exp(b*x - a*sq) / sq
3982 def _rvs(self, a, b, size=None, random_state=None):
3983 # note: X = b * V + sqrt(V) * X is norminvgaus(a,b) if X is standard
3984 # normal and V is invgauss(mu=1/sqrt(a**2 - b**2))
3985 gamma = np.sqrt(a**2 - b**2)
3986 ig = invgauss.rvs(mu=1/gamma, size=size, random_state=random_state)
3987 return b * ig + np.sqrt(ig) * norm.rvs(size=size, random_state=random_state)
3989 def _stats(self, a, b):
3990 gamma = np.sqrt(a**2 - b**2)
3991 mean = b / gamma
3992 variance = a**2 / gamma**3
3993 skewness = 3.0 * b / (a * np.sqrt(gamma))
3994 kurtosis = 3.0 * (1 + 4 * b**2 / a**2) / gamma
3995 return mean, variance, skewness, kurtosis
3998norminvgauss = norminvgauss_gen(name="norminvgauss")
4001class invweibull_gen(rv_continuous):
4002 u"""An inverted Weibull continuous random variable.
4004 This distribution is also known as the Fréchet distribution or the
4005 type II extreme value distribution.
4007 %(before_notes)s
4009 Notes
4010 -----
4011 The probability density function for `invweibull` is:
4013 .. math::
4015 f(x, c) = c x^{-c-1} \\exp(-x^{-c})
4017 for :math:`x > 0`, :math:`c > 0`.
4019 `invweibull` takes ``c`` as a shape parameter for :math:`c`.
4021 %(after_notes)s
4023 References
4024 ----------
4025 F.R.S. de Gusmao, E.M.M Ortega and G.M. Cordeiro, "The generalized inverse
4026 Weibull distribution", Stat. Papers, vol. 52, pp. 591-619, 2011.
4028 %(example)s
4030 """
4031 _support_mask = rv_continuous._open_support_mask
4033 def _pdf(self, x, c):
4034 # invweibull.pdf(x, c) = c * x**(-c-1) * exp(-x**(-c))
4035 xc1 = np.power(x, -c - 1.0)
4036 xc2 = np.power(x, -c)
4037 xc2 = np.exp(-xc2)
4038 return c * xc1 * xc2
4040 def _cdf(self, x, c):
4041 xc1 = np.power(x, -c)
4042 return np.exp(-xc1)
4044 def _ppf(self, q, c):
4045 return np.power(-np.log(q), -1.0/c)
4047 def _munp(self, n, c):
4048 return sc.gamma(1 - n / c)
4050 def _entropy(self, c):
4051 return 1+_EULER + _EULER / c - np.log(c)
4054invweibull = invweibull_gen(a=0, name='invweibull')
4057class johnsonsb_gen(rv_continuous):
4058 r"""A Johnson SB continuous random variable.
4060 %(before_notes)s
4062 See Also
4063 --------
4064 johnsonsu
4066 Notes
4067 -----
4068 The probability density function for `johnsonsb` is:
4070 .. math::
4072 f(x, a, b) = \frac{b}{x(1-x)} \phi(a + b \log \frac{x}{1-x} )
4074 for :math:`0 <= x < =1` and :math:`a, b > 0`, and :math:`\phi` is the normal
4075 pdf.
4077 `johnsonsb` takes :math:`a` and :math:`b` as shape parameters.
4079 %(after_notes)s
4081 %(example)s
4083 """
4084 _support_mask = rv_continuous._open_support_mask
4086 def _argcheck(self, a, b):
4087 return (b > 0) & (a == a)
4089 def _pdf(self, x, a, b):
4090 # johnsonsb.pdf(x, a, b) = b / (x*(1-x)) * phi(a + b * log(x/(1-x)))
4091 trm = _norm_pdf(a + b*np.log(x/(1.0-x)))
4092 return b*1.0/(x*(1-x))*trm
4094 def _cdf(self, x, a, b):
4095 return _norm_cdf(a + b*np.log(x/(1.0-x)))
4097 def _ppf(self, q, a, b):
4098 return 1.0 / (1 + np.exp(-1.0 / b * (_norm_ppf(q) - a)))
4101johnsonsb = johnsonsb_gen(a=0.0, b=1.0, name='johnsonsb')
4104class johnsonsu_gen(rv_continuous):
4105 r"""A Johnson SU continuous random variable.
4107 %(before_notes)s
4109 See Also
4110 --------
4111 johnsonsb
4113 Notes
4114 -----
4115 The probability density function for `johnsonsu` is:
4117 .. math::
4119 f(x, a, b) = \frac{b}{\sqrt{x^2 + 1}}
4120 \phi(a + b \log(x + \sqrt{x^2 + 1}))
4122 for all :math:`x, a, b > 0`, and :math:`\phi` is the normal pdf.
4124 `johnsonsu` takes :math:`a` and :math:`b` as shape parameters.
4126 %(after_notes)s
4128 %(example)s
4130 """
4131 def _argcheck(self, a, b):
4132 return (b > 0) & (a == a)
4134 def _pdf(self, x, a, b):
4135 # johnsonsu.pdf(x, a, b) = b / sqrt(x**2 + 1) *
4136 # phi(a + b * log(x + sqrt(x**2 + 1)))
4137 x2 = x*x
4138 trm = _norm_pdf(a + b * np.log(x + np.sqrt(x2+1)))
4139 return b*1.0/np.sqrt(x2+1.0)*trm
4141 def _cdf(self, x, a, b):
4142 return _norm_cdf(a + b * np.log(x + np.sqrt(x*x + 1)))
4144 def _ppf(self, q, a, b):
4145 return np.sinh((_norm_ppf(q) - a) / b)
4148johnsonsu = johnsonsu_gen(name='johnsonsu')
4151class laplace_gen(rv_continuous):
4152 r"""A Laplace continuous random variable.
4154 %(before_notes)s
4156 Notes
4157 -----
4158 The probability density function for `laplace` is
4160 .. math::
4162 f(x) = \frac{1}{2} \exp(-|x|)
4164 for a real number :math:`x`.
4166 %(after_notes)s
4168 %(example)s
4170 """
4171 def _rvs(self, size=None, random_state=None):
4172 return random_state.laplace(0, 1, size=size)
4174 def _pdf(self, x):
4175 # laplace.pdf(x) = 1/2 * exp(-abs(x))
4176 return 0.5*np.exp(-abs(x))
4178 def _cdf(self, x):
4179 return np.where(x > 0, 1.0-0.5*np.exp(-x), 0.5*np.exp(x))
4181 def _ppf(self, q):
4182 return np.where(q > 0.5, -np.log(2*(1-q)), np.log(2*q))
4184 def _stats(self):
4185 return 0, 2, 0, 3
4187 def _entropy(self):
4188 return np.log(2)+1
4190 @replace_notes_in_docstring(rv_continuous, notes="""\
4191 This function uses explicit formulas for the maximum likelihood
4192 estimation of the Laplace distribution parameters, so the keyword
4193 arguments `loc`, `scale`, and `optimizer` are ignored.\n\n""")
4194 def fit(self, data, *args, **kwds):
4195 floc = kwds.pop('floc', None)
4196 fscale = kwds.pop('fscale', None)
4198 _check_fit_input_parameters(data, args,
4199 kwds, fixed_param=(floc, fscale))
4201 # MLE for the laplace distribution
4203 if floc is None:
4204 loc = np.median(data)
4205 else:
4206 loc = floc
4208 if fscale is None:
4209 scale = (np.sum(np.abs(data - loc))) / len(data)
4210 else:
4211 scale = fscale
4213 # Source: Statistical Distributions, 3rd Edition. Evans, Hastings,
4214 # and Peacock (2000), Page 124
4216 return loc, scale
4219laplace = laplace_gen(name='laplace')
4222def _check_fit_input_parameters(data, args, kwds, fixed_param):
4223 if len(args) > 0:
4224 raise TypeError("Too many arguments.")
4226 _remove_optimizer_parameters(kwds)
4228 if None not in fixed_param:
4229 # This check is for consistency with `rv_continuous.fit`.
4230 # Without this check, this function would just return the
4231 # parameters that were given.
4232 raise RuntimeError("All parameters fixed. There is nothing to "
4233 "optimize.")
4235 data = np.asarray(data)
4236 if not np.isfinite(data).all():
4237 raise RuntimeError("The data contains non-finite values.")
4240class levy_gen(rv_continuous):
4241 r"""A Levy continuous random variable.
4243 %(before_notes)s
4245 See Also
4246 --------
4247 levy_stable, levy_l
4249 Notes
4250 -----
4251 The probability density function for `levy` is:
4253 .. math::
4255 f(x) = \frac{1}{\sqrt{2\pi x^3}} \exp\left(-\frac{1}{2x}\right)
4257 for :math:`x >= 0`.
4259 This is the same as the Levy-stable distribution with :math:`a=1/2` and
4260 :math:`b=1`.
4262 %(after_notes)s
4264 %(example)s
4266 """
4267 _support_mask = rv_continuous._open_support_mask
4269 def _pdf(self, x):
4270 # levy.pdf(x) = 1 / (x * sqrt(2*pi*x)) * exp(-1/(2*x))
4271 return 1 / np.sqrt(2*np.pi*x) / x * np.exp(-1/(2*x))
4273 def _cdf(self, x):
4274 # Equivalent to 2*norm.sf(np.sqrt(1/x))
4275 return sc.erfc(np.sqrt(0.5 / x))
4277 def _ppf(self, q):
4278 # Equivalent to 1.0/(norm.isf(q/2)**2) or 0.5/(erfcinv(q)**2)
4279 val = -sc.ndtri(q/2)
4280 return 1.0 / (val * val)
4282 def _stats(self):
4283 return np.inf, np.inf, np.nan, np.nan
4286levy = levy_gen(a=0.0, name="levy")
4289class levy_l_gen(rv_continuous):
4290 r"""A left-skewed Levy continuous random variable.
4292 %(before_notes)s
4294 See Also
4295 --------
4296 levy, levy_stable
4298 Notes
4299 -----
4300 The probability density function for `levy_l` is:
4302 .. math::
4303 f(x) = \frac{1}{|x| \sqrt{2\pi |x|}} \exp{ \left(-\frac{1}{2|x|} \right)}
4305 for :math:`x <= 0`.
4307 This is the same as the Levy-stable distribution with :math:`a=1/2` and
4308 :math:`b=-1`.
4310 %(after_notes)s
4312 %(example)s
4314 """
4315 _support_mask = rv_continuous._open_support_mask
4317 def _pdf(self, x):
4318 # levy_l.pdf(x) = 1 / (abs(x) * sqrt(2*pi*abs(x))) * exp(-1/(2*abs(x)))
4319 ax = abs(x)
4320 return 1/np.sqrt(2*np.pi*ax)/ax*np.exp(-1/(2*ax))
4322 def _cdf(self, x):
4323 ax = abs(x)
4324 return 2 * _norm_cdf(1 / np.sqrt(ax)) - 1
4326 def _ppf(self, q):
4327 val = _norm_ppf((q + 1.0) / 2)
4328 return -1.0 / (val * val)
4330 def _stats(self):
4331 return np.inf, np.inf, np.nan, np.nan
4334levy_l = levy_l_gen(b=0.0, name="levy_l")
4337class levy_stable_gen(rv_continuous):
4338 r"""A Levy-stable continuous random variable.
4340 %(before_notes)s
4342 See Also
4343 --------
4344 levy, levy_l
4346 Notes
4347 -----
4348 The distribution for `levy_stable` has characteristic function:
4350 .. math::
4352 \varphi(t, \alpha, \beta, c, \mu) =
4353 e^{it\mu -|ct|^{\alpha}(1-i\beta \operatorname{sign}(t)\Phi(\alpha, t))}
4355 where:
4357 .. math::
4359 \Phi = \begin{cases}
4360 \tan \left({\frac {\pi \alpha }{2}}\right)&\alpha \neq 1\\
4361 -{\frac {2}{\pi }}\log |t|&\alpha =1
4362 \end{cases}
4364 The probability density function for `levy_stable` is:
4366 .. math::
4368 f(x) = \frac{1}{2\pi}\int_{-\infty}^\infty \varphi(t)e^{-ixt}\,dt
4370 where :math:`-\infty < t < \infty`. This integral does not have a known closed form.
4372 For evaluation of pdf we use either Zolotarev :math:`S_0` parameterization with integration,
4373 direct integration of standard parameterization of characteristic function or FFT of
4374 characteristic function. If set to other than None and if number of points is greater than
4375 ``levy_stable.pdf_fft_min_points_threshold`` (defaults to None) we use FFT otherwise we use one
4376 of the other methods.
4378 The default method is 'best' which uses Zolotarev's method if alpha = 1 and integration of
4379 characteristic function otherwise. The default method can be changed by setting
4380 ``levy_stable.pdf_default_method`` to either 'zolotarev', 'quadrature' or 'best'.
4382 To increase accuracy of FFT calculation one can specify ``levy_stable.pdf_fft_grid_spacing``
4383 (defaults to 0.001) and ``pdf_fft_n_points_two_power`` (defaults to a value that covers the
4384 input range * 4). Setting ``pdf_fft_n_points_two_power`` to 16 should be sufficiently accurate
4385 in most cases at the expense of CPU time.
4387 For evaluation of cdf we use Zolatarev :math:`S_0` parameterization with integration or integral of
4388 the pdf FFT interpolated spline. The settings affecting FFT calculation are the same as
4389 for pdf calculation. Setting the threshold to ``None`` (default) will disable FFT. For cdf
4390 calculations the Zolatarev method is superior in accuracy, so FFT is disabled by default.
4392 Fitting estimate uses quantile estimation method in [MC]. MLE estimation of parameters in
4393 fit method uses this quantile estimate initially. Note that MLE doesn't always converge if
4394 using FFT for pdf calculations; so it's best that ``pdf_fft_min_points_threshold`` is left unset.
4396 .. warning::
4398 For pdf calculations implementation of Zolatarev is unstable for values where alpha = 1 and
4399 beta != 0. In this case the quadrature method is recommended. FFT calculation is also
4400 considered experimental.
4402 For cdf calculations FFT calculation is considered experimental. Use Zolatarev's method
4403 instead (default).
4405 %(after_notes)s
4407 References
4408 ----------
4409 .. [MC] McCulloch, J., 1986. Simple consistent estimators of stable distribution parameters.
4410 Communications in Statistics - Simulation and Computation 15, 11091136.
4411 .. [MS] Mittnik, S.T. Rachev, T. Doganoglu, D. Chenyao, 1999. Maximum likelihood estimation
4412 of stable Paretian models, Mathematical and Computer Modelling, Volume 29, Issue 10,
4413 1999, Pages 275-293.
4414 .. [BS] Borak, S., Hardle, W., Rafal, W. 2005. Stable distributions, Economic Risk.
4416 %(example)s
4418 """
4420 def _rvs(self, alpha, beta, size=None, random_state=None):
4422 def alpha1func(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W):
4423 return (2/np.pi*(np.pi/2 + bTH)*tanTH -
4424 beta*np.log((np.pi/2*W*cosTH)/(np.pi/2 + bTH)))
4426 def beta0func(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W):
4427 return (W/(cosTH/np.tan(aTH) + np.sin(TH)) *
4428 ((np.cos(aTH) + np.sin(aTH)*tanTH)/W)**(1.0/alpha))
4430 def otherwise(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W):
4431 # alpha is not 1 and beta is not 0
4432 val0 = beta*np.tan(np.pi*alpha/2)
4433 th0 = np.arctan(val0)/alpha
4434 val3 = W/(cosTH/np.tan(alpha*(th0 + TH)) + np.sin(TH))
4435 res3 = val3*((np.cos(aTH) + np.sin(aTH)*tanTH -
4436 val0*(np.sin(aTH) - np.cos(aTH)*tanTH))/W)**(1.0/alpha)
4437 return res3
4439 def alphanot1func(alpha, beta, TH, aTH, bTH, cosTH, tanTH, W):
4440 res = _lazywhere(beta == 0,
4441 (alpha, beta, TH, aTH, bTH, cosTH, tanTH, W),
4442 beta0func, f2=otherwise)
4443 return res
4445 alpha = np.broadcast_to(alpha, size)
4446 beta = np.broadcast_to(beta, size)
4447 TH = uniform.rvs(loc=-np.pi/2.0, scale=np.pi, size=size,
4448 random_state=random_state)
4449 W = expon.rvs(size=size, random_state=random_state)
4450 aTH = alpha*TH
4451 bTH = beta*TH
4452 cosTH = np.cos(TH)
4453 tanTH = np.tan(TH)
4454 res = _lazywhere(alpha == 1,
4455 (alpha, beta, TH, aTH, bTH, cosTH, tanTH, W),
4456 alpha1func, f2=alphanot1func)
4457 return res
4459 def _argcheck(self, alpha, beta):
4460 return (alpha > 0) & (alpha <= 2) & (beta <= 1) & (beta >= -1)
4462 @staticmethod
4463 def _cf(t, alpha, beta):
4464 Phi = lambda alpha, t: np.tan(np.pi*alpha/2) if alpha != 1 else -2.0*np.log(np.abs(t))/np.pi
4465 return np.exp(-(np.abs(t)**alpha)*(1-1j*beta*np.sign(t)*Phi(alpha, t)))
4467 @staticmethod
4468 def _pdf_from_cf_with_fft(cf, h=0.01, q=9):
4469 """Calculates pdf from cf using fft. Using region around 0 with N=2**q points
4470 separated by distance h. As suggested by [MS].
4471 """
4472 N = 2**q
4473 n = np.arange(1,N+1)
4474 density = ((-1)**(n-1-N/2))*np.fft.fft(((-1)**(n-1))*cf(2*np.pi*(n-1-N/2)/h/N))/h/N
4475 x = (n-1-N/2)*h
4476 return (x, density)
4478 @staticmethod
4479 def _pdf_single_value_best(x, alpha, beta):
4480 if alpha != 1. or (alpha == 1. and beta == 0.):
4481 return levy_stable_gen._pdf_single_value_zolotarev(x, alpha, beta)
4482 else:
4483 return levy_stable_gen._pdf_single_value_cf_integrate(x, alpha, beta)
4485 @staticmethod
4486 def _pdf_single_value_cf_integrate(x, alpha, beta):
4487 cf = lambda t: levy_stable_gen._cf(t, alpha, beta)
4488 return integrate.quad(lambda t: np.real(np.exp(-1j*t*x)*cf(t)), -np.inf, np.inf, limit=1000)[0]/np.pi/2
4490 @staticmethod
4491 def _pdf_single_value_zolotarev(x, alpha, beta):
4492 """Calculate pdf using Zolotarev's methods as detailed in [BS].
4493 """
4494 zeta = -beta*np.tan(np.pi*alpha/2.)
4495 if alpha != 1:
4496 x0 = x + zeta # convert to S_0 parameterization
4497 xi = np.arctan(-zeta)/alpha
4499 def V(theta):
4500 return np.cos(alpha*xi)**(1/(alpha-1)) * \
4501 (np.cos(theta)/np.sin(alpha*(xi+theta)))**(alpha/(alpha-1)) * \
4502 (np.cos(alpha*xi+(alpha-1)*theta)/np.cos(theta))
4503 if x0 > zeta:
4504 def g(theta):
4505 return V(theta)*np.real(np.complex(x0-zeta)**(alpha/(alpha-1)))
4507 def f(theta):
4508 return g(theta) * np.exp(-g(theta))
4510 # spare calculating integral on null set
4511 # use isclose as macos has fp differences
4512 if np.isclose(-xi, np.pi/2, rtol=1e-014, atol=1e-014):
4513 return 0.
4515 with np.errstate(all="ignore"):
4516 intg_max = optimize.minimize_scalar(lambda theta: -f(theta), bounds=[-xi, np.pi/2])
4517 intg_kwargs = {}
4518 # windows quadpack less forgiving with points out of bounds
4519 if intg_max.success and not np.isnan(intg_max.fun)\
4520 and intg_max.x > -xi and intg_max.x < np.pi/2:
4521 intg_kwargs["points"] = [intg_max.x]
4522 intg = integrate.quad(f, -xi, np.pi/2, **intg_kwargs)[0]
4523 return alpha * intg / np.pi / np.abs(alpha-1) / (x0-zeta)
4524 elif x0 == zeta:
4525 return sc.gamma(1+1/alpha)*np.cos(xi)/np.pi/((1+zeta**2)**(1/alpha/2))
4526 else:
4527 return levy_stable_gen._pdf_single_value_zolotarev(-x, alpha, -beta)
4528 else:
4529 # since location zero, no need to reposition x for S_0 parameterization
4530 xi = np.pi/2
4531 if beta != 0:
4532 warnings.warn('Density calculation unstable for alpha=1 and beta!=0.' +
4533 ' Use quadrature method instead.', RuntimeWarning)
4535 def V(theta):
4536 expr_1 = np.pi/2+beta*theta
4537 return 2. * expr_1 * np.exp(expr_1*np.tan(theta)/beta) / np.cos(theta) / np.pi
4539 def g(theta):
4540 return np.exp(-np.pi * x / 2. / beta) * V(theta)
4542 def f(theta):
4543 return g(theta) * np.exp(-g(theta))
4545 with np.errstate(all="ignore"):
4546 intg_max = optimize.minimize_scalar(lambda theta: -f(theta), bounds=[-np.pi/2, np.pi/2])
4547 intg = integrate.fixed_quad(f, -np.pi/2, intg_max.x)[0] + integrate.fixed_quad(f, intg_max.x, np.pi/2)[0]
4548 return intg / np.abs(beta) / 2.
4549 else:
4550 return 1/(1+x**2)/np.pi
4552 @staticmethod
4553 def _cdf_single_value_zolotarev(x, alpha, beta):
4554 """Calculate cdf using Zolotarev's methods as detailed in [BS].
4555 """
4556 zeta = -beta*np.tan(np.pi*alpha/2.)
4557 if alpha != 1:
4558 x0 = x + zeta # convert to S_0 parameterization
4559 xi = np.arctan(-zeta)/alpha
4561 def V(theta):
4562 return np.cos(alpha*xi)**(1/(alpha-1)) * \
4563 (np.cos(theta)/np.sin(alpha*(xi+theta)))**(alpha/(alpha-1)) * \
4564 (np.cos(alpha*xi+(alpha-1)*theta)/np.cos(theta))
4565 if x0 > zeta:
4566 c_1 = 1 if alpha > 1 else .5 - xi/np.pi
4568 def f(theta):
4569 return np.exp(-V(theta)*np.real(np.complex(x0-zeta)**(alpha/(alpha-1))))
4571 with np.errstate(all="ignore"):
4572 # spare calculating integral on null set
4573 # use isclose as macos has fp differences
4574 if np.isclose(-xi, np.pi/2, rtol=1e-014, atol=1e-014):
4575 intg = 0
4576 else:
4577 intg = integrate.quad(f, -xi, np.pi/2)[0]
4578 return c_1 + np.sign(1-alpha) * intg / np.pi
4579 elif x0 == zeta:
4580 return .5 - xi/np.pi
4581 else:
4582 return 1 - levy_stable_gen._cdf_single_value_zolotarev(-x, alpha, -beta)
4584 else:
4585 # since location zero, no need to reposition x for S_0 parameterization
4586 xi = np.pi/2
4587 if beta > 0:
4589 def V(theta):
4590 expr_1 = np.pi/2+beta*theta
4591 return 2. * expr_1 * np.exp(expr_1*np.tan(theta)/beta) / np.cos(theta) / np.pi
4593 with np.errstate(all="ignore"):
4594 expr_1 = np.exp(-np.pi*x/beta/2.)
4595 int_1 = integrate.quad(lambda theta: np.exp(-expr_1 * V(theta)), -np.pi/2, np.pi/2)[0]
4596 return int_1 / np.pi
4597 elif beta == 0:
4598 return .5 + np.arctan(x)/np.pi
4599 else:
4600 return 1 - levy_stable_gen._cdf_single_value_zolotarev(-x, 1, -beta)
4602 def _pdf(self, x, alpha, beta):
4604 x = np.asarray(x).reshape(1, -1)[0,:]
4606 x, alpha, beta = np.broadcast_arrays(x, alpha, beta)
4608 data_in = np.dstack((x, alpha, beta))[0]
4609 data_out = np.empty(shape=(len(data_in),1))
4611 pdf_default_method_name = getattr(self, 'pdf_default_method', 'best')
4612 if pdf_default_method_name == 'best':
4613 pdf_single_value_method = levy_stable_gen._pdf_single_value_best
4614 elif pdf_default_method_name == 'zolotarev':
4615 pdf_single_value_method = levy_stable_gen._pdf_single_value_zolotarev
4616 else:
4617 pdf_single_value_method = levy_stable_gen._pdf_single_value_cf_integrate
4619 fft_min_points_threshold = getattr(self, 'pdf_fft_min_points_threshold', None)
4620 fft_grid_spacing = getattr(self, 'pdf_fft_grid_spacing', 0.001)
4621 fft_n_points_two_power = getattr(self, 'pdf_fft_n_points_two_power', None)
4623 # group data in unique arrays of alpha, beta pairs
4624 uniq_param_pairs = np.vstack(list({tuple(row) for row in
4625 data_in[:, 1:]}))
4626 for pair in uniq_param_pairs:
4627 data_mask = np.all(data_in[:,1:] == pair, axis=-1)
4628 data_subset = data_in[data_mask]
4629 if fft_min_points_threshold is None or len(data_subset) < fft_min_points_threshold:
4630 data_out[data_mask] = np.array([pdf_single_value_method(_x, _alpha, _beta)
4631 for _x, _alpha, _beta in data_subset]).reshape(len(data_subset), 1)
4632 else:
4633 warnings.warn('Density calculations experimental for FFT method.' +
4634 ' Use combination of zolatarev and quadrature methods instead.', RuntimeWarning)
4635 _alpha, _beta = pair
4636 _x = data_subset[:,(0,)]
4638 # need enough points to "cover" _x for interpolation
4639 h = fft_grid_spacing
4640 q = np.ceil(np.log(2*np.max(np.abs(_x))/h)/np.log(2)) + 2 if fft_n_points_two_power is None else int(fft_n_points_two_power)
4642 density_x, density = levy_stable_gen._pdf_from_cf_with_fft(lambda t: levy_stable_gen._cf(t, _alpha, _beta), h=h, q=q)
4643 f = interpolate.interp1d(density_x, np.real(density))
4644 data_out[data_mask] = f(_x)
4646 return data_out.T[0]
4648 def _cdf(self, x, alpha, beta):
4650 x = np.asarray(x).reshape(1, -1)[0,:]
4652 x, alpha, beta = np.broadcast_arrays(x, alpha, beta)
4654 data_in = np.dstack((x, alpha, beta))[0]
4655 data_out = np.empty(shape=(len(data_in),1))
4657 fft_min_points_threshold = getattr(self, 'pdf_fft_min_points_threshold', None)
4658 fft_grid_spacing = getattr(self, 'pdf_fft_grid_spacing', 0.001)
4659 fft_n_points_two_power = getattr(self, 'pdf_fft_n_points_two_power', None)
4661 # group data in unique arrays of alpha, beta pairs
4662 uniq_param_pairs = np.vstack(
4663 list({tuple(row) for row in data_in[:,1:]}))
4664 for pair in uniq_param_pairs:
4665 data_mask = np.all(data_in[:,1:] == pair, axis=-1)
4666 data_subset = data_in[data_mask]
4667 if fft_min_points_threshold is None or len(data_subset) < fft_min_points_threshold:
4668 data_out[data_mask] = np.array([levy_stable._cdf_single_value_zolotarev(_x, _alpha, _beta)
4669 for _x, _alpha, _beta in data_subset]).reshape(len(data_subset), 1)
4670 else:
4671 warnings.warn(u'FFT method is considered experimental for ' +
4672 u'cumulative distribution function ' +
4673 u'evaluations. Use Zolotarev’s method instead).',
4674 RuntimeWarning)
4675 _alpha, _beta = pair
4676 _x = data_subset[:,(0,)]
4678 # need enough points to "cover" _x for interpolation
4679 h = fft_grid_spacing
4680 q = 16 if fft_n_points_two_power is None else int(fft_n_points_two_power)
4682 density_x, density = levy_stable_gen._pdf_from_cf_with_fft(lambda t: levy_stable_gen._cf(t, _alpha, _beta), h=h, q=q)
4683 f = interpolate.InterpolatedUnivariateSpline(density_x, np.real(density))
4684 data_out[data_mask] = np.array([f.integral(self.a, x_1) for x_1 in _x]).reshape(data_out[data_mask].shape)
4686 return data_out.T[0]
4688 def _fitstart(self, data):
4689 # We follow McCullock 1986 method - Simple Consistent Estimators
4690 # of Stable Distribution Parameters
4692 # Table III and IV
4693 nu_alpha_range = [2.439, 2.5, 2.6, 2.7, 2.8, 3, 3.2, 3.5, 4, 5, 6, 8, 10, 15, 25]
4694 nu_beta_range = [0, 0.1, 0.2, 0.3, 0.5, 0.7, 1]
4696 # table III - alpha = psi_1(nu_alpha, nu_beta)
4697 alpha_table = [
4698 [2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000],
4699 [1.916, 1.924, 1.924, 1.924, 1.924, 1.924, 1.924],
4700 [1.808, 1.813, 1.829, 1.829, 1.829, 1.829, 1.829],
4701 [1.729, 1.730, 1.737, 1.745, 1.745, 1.745, 1.745],
4702 [1.664, 1.663, 1.663, 1.668, 1.676, 1.676, 1.676],
4703 [1.563, 1.560, 1.553, 1.548, 1.547, 1.547, 1.547],
4704 [1.484, 1.480, 1.471, 1.460, 1.448, 1.438, 1.438],
4705 [1.391, 1.386, 1.378, 1.364, 1.337, 1.318, 1.318],
4706 [1.279, 1.273, 1.266, 1.250, 1.210, 1.184, 1.150],
4707 [1.128, 1.121, 1.114, 1.101, 1.067, 1.027, 0.973],
4708 [1.029, 1.021, 1.014, 1.004, 0.974, 0.935, 0.874],
4709 [0.896, 0.892, 0.884, 0.883, 0.855, 0.823, 0.769],
4710 [0.818, 0.812, 0.806, 0.801, 0.780, 0.756, 0.691],
4711 [0.698, 0.695, 0.692, 0.689, 0.676, 0.656, 0.597],
4712 [0.593, 0.590, 0.588, 0.586, 0.579, 0.563, 0.513]]
4714 # table IV - beta = psi_2(nu_alpha, nu_beta)
4715 beta_table = [
4716 [0, 2.160, 1.000, 1.000, 1.000, 1.000, 1.000],
4717 [0, 1.592, 3.390, 1.000, 1.000, 1.000, 1.000],
4718 [0, 0.759, 1.800, 1.000, 1.000, 1.000, 1.000],
4719 [0, 0.482, 1.048, 1.694, 1.000, 1.000, 1.000],
4720 [0, 0.360, 0.760, 1.232, 2.229, 1.000, 1.000],
4721 [0, 0.253, 0.518, 0.823, 1.575, 1.000, 1.000],
4722 [0, 0.203, 0.410, 0.632, 1.244, 1.906, 1.000],
4723 [0, 0.165, 0.332, 0.499, 0.943, 1.560, 1.000],
4724 [0, 0.136, 0.271, 0.404, 0.689, 1.230, 2.195],
4725 [0, 0.109, 0.216, 0.323, 0.539, 0.827, 1.917],
4726 [0, 0.096, 0.190, 0.284, 0.472, 0.693, 1.759],
4727 [0, 0.082, 0.163, 0.243, 0.412, 0.601, 1.596],
4728 [0, 0.074, 0.147, 0.220, 0.377, 0.546, 1.482],
4729 [0, 0.064, 0.128, 0.191, 0.330, 0.478, 1.362],
4730 [0, 0.056, 0.112, 0.167, 0.285, 0.428, 1.274]]
4732 # Table V and VII
4733 alpha_range = [2, 1.9, 1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1, 1, 0.9, 0.8, 0.7, 0.6, 0.5]
4734 beta_range = [0, 0.25, 0.5, 0.75, 1]
4736 # Table V - nu_c = psi_3(alpha, beta)
4737 nu_c_table = [
4738 [1.908, 1.908, 1.908, 1.908, 1.908],
4739 [1.914, 1.915, 1.916, 1.918, 1.921],
4740 [1.921, 1.922, 1.927, 1.936, 1.947],
4741 [1.927, 1.930, 1.943, 1.961, 1.987],
4742 [1.933, 1.940, 1.962, 1.997, 2.043],
4743 [1.939, 1.952, 1.988, 2.045, 2.116],
4744 [1.946, 1.967, 2.022, 2.106, 2.211],
4745 [1.955, 1.984, 2.067, 2.188, 2.333],
4746 [1.965, 2.007, 2.125, 2.294, 2.491],
4747 [1.980, 2.040, 2.205, 2.435, 2.696],
4748 [2.000, 2.085, 2.311, 2.624, 2.973],
4749 [2.040, 2.149, 2.461, 2.886, 3.356],
4750 [2.098, 2.244, 2.676, 3.265, 3.912],
4751 [2.189, 2.392, 3.004, 3.844, 4.775],
4752 [2.337, 2.634, 3.542, 4.808, 6.247],
4753 [2.588, 3.073, 4.534, 6.636, 9.144]]
4755 # Table VII - nu_zeta = psi_5(alpha, beta)
4756 nu_zeta_table = [
4757 [0, 0.000, 0.000, 0.000, 0.000],
4758 [0, -0.017, -0.032, -0.049, -0.064],
4759 [0, -0.030, -0.061, -0.092, -0.123],
4760 [0, -0.043, -0.088, -0.132, -0.179],
4761 [0, -0.056, -0.111, -0.170, -0.232],
4762 [0, -0.066, -0.134, -0.206, -0.283],
4763 [0, -0.075, -0.154, -0.241, -0.335],
4764 [0, -0.084, -0.173, -0.276, -0.390],
4765 [0, -0.090, -0.192, -0.310, -0.447],
4766 [0, -0.095, -0.208, -0.346, -0.508],
4767 [0, -0.098, -0.223, -0.380, -0.576],
4768 [0, -0.099, -0.237, -0.424, -0.652],
4769 [0, -0.096, -0.250, -0.469, -0.742],
4770 [0, -0.089, -0.262, -0.520, -0.853],
4771 [0, -0.078, -0.272, -0.581, -0.997],
4772 [0, -0.061, -0.279, -0.659, -1.198]]
4774 psi_1 = interpolate.interp2d(nu_beta_range, nu_alpha_range, alpha_table, kind='linear')
4775 psi_2 = interpolate.interp2d(nu_beta_range, nu_alpha_range, beta_table, kind='linear')
4776 psi_2_1 = lambda nu_beta, nu_alpha: psi_2(nu_beta, nu_alpha) if nu_beta > 0 else -psi_2(-nu_beta, nu_alpha)
4778 phi_3 = interpolate.interp2d(beta_range, alpha_range, nu_c_table, kind='linear')
4779 phi_3_1 = lambda beta, alpha: phi_3(beta, alpha) if beta > 0 else phi_3(-beta, alpha)
4780 phi_5 = interpolate.interp2d(beta_range, alpha_range, nu_zeta_table, kind='linear')
4781 phi_5_1 = lambda beta, alpha: phi_5(beta, alpha) if beta > 0 else -phi_5(-beta, alpha)
4783 # quantiles
4784 p05 = np.percentile(data, 5)
4785 p50 = np.percentile(data, 50)
4786 p95 = np.percentile(data, 95)
4787 p25 = np.percentile(data, 25)
4788 p75 = np.percentile(data, 75)
4790 nu_alpha = (p95 - p05)/(p75 - p25)
4791 nu_beta = (p95 + p05 - 2*p50)/(p95 - p05)
4793 if nu_alpha >= 2.439:
4794 alpha = np.clip(psi_1(nu_beta, nu_alpha)[0], np.finfo(float).eps, 2.)
4795 beta = np.clip(psi_2_1(nu_beta, nu_alpha)[0], -1., 1.)
4796 else:
4797 alpha = 2.0
4798 beta = np.sign(nu_beta)
4799 c = (p75 - p25) / phi_3_1(beta, alpha)[0]
4800 zeta = p50 + c*phi_5_1(beta, alpha)[0]
4801 delta = np.clip(zeta-beta*c*np.tan(np.pi*alpha/2.) if alpha == 1. else zeta, np.finfo(float).eps, np.inf)
4803 return (alpha, beta, delta, c)
4805 def _stats(self, alpha, beta):
4806 mu = 0 if alpha > 1 else np.nan
4807 mu2 = 2 if alpha == 2 else np.inf
4808 g1 = 0. if alpha == 2. else np.NaN
4809 g2 = 0. if alpha == 2. else np.NaN
4810 return mu, mu2, g1, g2
4813levy_stable = levy_stable_gen(name='levy_stable')
4816class logistic_gen(rv_continuous):
4817 r"""A logistic (or Sech-squared) continuous random variable.
4819 %(before_notes)s
4821 Notes
4822 -----
4823 The probability density function for `logistic` is:
4825 .. math::
4827 f(x) = \frac{\exp(-x)}
4828 {(1+\exp(-x))^2}
4830 `logistic` is a special case of `genlogistic` with ``c=1``.
4832 %(after_notes)s
4834 %(example)s
4836 """
4837 def _rvs(self, size=None, random_state=None):
4838 return random_state.logistic(size=size)
4840 def _pdf(self, x):
4841 # logistic.pdf(x) = exp(-x) / (1+exp(-x))**2
4842 return np.exp(self._logpdf(x))
4844 def _logpdf(self, x):
4845 return -x - 2. * sc.log1p(np.exp(-x))
4847 def _cdf(self, x):
4848 return sc.expit(x)
4850 def _ppf(self, q):
4851 return sc.logit(q)
4853 def _sf(self, x):
4854 return sc.expit(-x)
4856 def _isf(self, q):
4857 return -sc.logit(q)
4859 def _stats(self):
4860 return 0, np.pi*np.pi/3.0, 0, 6.0/5.0
4862 def _entropy(self):
4863 # https://en.wikipedia.org/wiki/Logistic_distribution
4864 return 2.0
4867logistic = logistic_gen(name='logistic')
4870class loggamma_gen(rv_continuous):
4871 r"""A log gamma continuous random variable.
4873 %(before_notes)s
4875 Notes
4876 -----
4877 The probability density function for `loggamma` is:
4879 .. math::
4881 f(x, c) = \frac{\exp(c x - \exp(x))}
4882 {\Gamma(c)}
4884 for all :math:`x, c > 0`. Here, :math:`\Gamma` is the
4885 gamma function (`scipy.special.gamma`).
4887 `loggamma` takes ``c`` as a shape parameter for :math:`c`.
4889 %(after_notes)s
4891 %(example)s
4893 """
4894 def _rvs(self, c, size=None, random_state=None):
4895 return np.log(random_state.gamma(c, size=size))
4897 def _pdf(self, x, c):
4898 # loggamma.pdf(x, c) = exp(c*x-exp(x)) / gamma(c)
4899 return np.exp(c*x-np.exp(x)-sc.gammaln(c))
4901 def _cdf(self, x, c):
4902 return sc.gammainc(c, np.exp(x))
4904 def _ppf(self, q, c):
4905 return np.log(sc.gammaincinv(c, q))
4907 def _stats(self, c):
4908 # See, for example, "A Statistical Study of Log-Gamma Distribution", by
4909 # Ping Shing Chan (thesis, McMaster University, 1993).
4910 mean = sc.digamma(c)
4911 var = sc.polygamma(1, c)
4912 skewness = sc.polygamma(2, c) / np.power(var, 1.5)
4913 excess_kurtosis = sc.polygamma(3, c) / (var*var)
4914 return mean, var, skewness, excess_kurtosis
4917loggamma = loggamma_gen(name='loggamma')
4920class loglaplace_gen(rv_continuous):
4921 r"""A log-Laplace continuous random variable.
4923 %(before_notes)s
4925 Notes
4926 -----
4927 The probability density function for `loglaplace` is:
4929 .. math::
4931 f(x, c) = \begin{cases}\frac{c}{2} x^{ c-1} &\text{for } 0 < x < 1\\
4932 \frac{c}{2} x^{-c-1} &\text{for } x \ge 1
4933 \end{cases}
4935 for :math:`c > 0`.
4937 `loglaplace` takes ``c`` as a shape parameter for :math:`c`.
4939 %(after_notes)s
4941 References
4942 ----------
4943 T.J. Kozubowski and K. Podgorski, "A log-Laplace growth rate model",
4944 The Mathematical Scientist, vol. 28, pp. 49-60, 2003.
4946 %(example)s
4948 """
4949 def _pdf(self, x, c):
4950 # loglaplace.pdf(x, c) = c / 2 * x**(c-1), for 0 < x < 1
4951 # = c / 2 * x**(-c-1), for x >= 1
4952 cd2 = c/2.0
4953 c = np.where(x < 1, c, -c)
4954 return cd2*x**(c-1)
4956 def _cdf(self, x, c):
4957 return np.where(x < 1, 0.5*x**c, 1-0.5*x**(-c))
4959 def _ppf(self, q, c):
4960 return np.where(q < 0.5, (2.0*q)**(1.0/c), (2*(1.0-q))**(-1.0/c))
4962 def _munp(self, n, c):
4963 return c**2 / (c**2 - n**2)
4965 def _entropy(self, c):
4966 return np.log(2.0/c) + 1.0
4969loglaplace = loglaplace_gen(a=0.0, name='loglaplace')
4972def _lognorm_logpdf(x, s):
4973 return _lazywhere(x != 0, (x, s),
4974 lambda x, s: -np.log(x)**2 / (2*s**2) - np.log(s*x*np.sqrt(2*np.pi)),
4975 -np.inf)
4978class lognorm_gen(rv_continuous):
4979 r"""A lognormal continuous random variable.
4981 %(before_notes)s
4983 Notes
4984 -----
4985 The probability density function for `lognorm` is:
4987 .. math::
4989 f(x, s) = \frac{1}{s x \sqrt{2\pi}}
4990 \exp\left(-\frac{\log^2(x)}{2s^2}\right)
4992 for :math:`x > 0`, :math:`s > 0`.
4994 `lognorm` takes ``s`` as a shape parameter for :math:`s`.
4996 %(after_notes)s
4998 A common parametrization for a lognormal random variable ``Y`` is in
4999 terms of the mean, ``mu``, and standard deviation, ``sigma``, of the
5000 unique normally distributed random variable ``X`` such that exp(X) = Y.
5001 This parametrization corresponds to setting ``s = sigma`` and ``scale =
5002 exp(mu)``.
5004 %(example)s
5006 """
5007 _support_mask = rv_continuous._open_support_mask
5009 def _rvs(self, s, size=None, random_state=None):
5010 return np.exp(s * random_state.standard_normal(size))
5012 def _pdf(self, x, s):
5013 # lognorm.pdf(x, s) = 1 / (s*x*sqrt(2*pi)) * exp(-1/2*(log(x)/s)**2)
5014 return np.exp(self._logpdf(x, s))
5016 def _logpdf(self, x, s):
5017 return _lognorm_logpdf(x, s)
5019 def _cdf(self, x, s):
5020 return _norm_cdf(np.log(x) / s)
5022 def _logcdf(self, x, s):
5023 return _norm_logcdf(np.log(x) / s)
5025 def _ppf(self, q, s):
5026 return np.exp(s * _norm_ppf(q))
5028 def _sf(self, x, s):
5029 return _norm_sf(np.log(x) / s)
5031 def _logsf(self, x, s):
5032 return _norm_logsf(np.log(x) / s)
5034 def _stats(self, s):
5035 p = np.exp(s*s)
5036 mu = np.sqrt(p)
5037 mu2 = p*(p-1)
5038 g1 = np.sqrt((p-1))*(2+p)
5039 g2 = np.polyval([1, 2, 3, 0, -6.0], p)
5040 return mu, mu2, g1, g2
5042 def _entropy(self, s):
5043 return 0.5 * (1 + np.log(2*np.pi) + 2 * np.log(s))
5045 @extend_notes_in_docstring(rv_continuous, notes="""\
5046 When the location parameter is fixed by using the `floc` argument,
5047 this function uses explicit formulas for the maximum likelihood
5048 estimation of the log-normal shape and scale parameters, so the
5049 `optimizer`, `loc` and `scale` keyword arguments are ignored.\n\n""")
5050 def fit(self, data, *args, **kwds):
5051 floc = kwds.get('floc', None)
5052 if floc is None:
5053 # loc is not fixed. Use the default fit method.
5054 return super(lognorm_gen, self).fit(data, *args, **kwds)
5056 f0 = (kwds.get('f0', None) or kwds.get('fs', None) or
5057 kwds.get('fix_s', None))
5058 fscale = kwds.get('fscale', None)
5060 if len(args) > 1:
5061 raise TypeError("Too many input arguments.")
5062 for name in ['f0', 'fs', 'fix_s', 'floc', 'fscale', 'loc', 'scale',
5063 'optimizer']:
5064 kwds.pop(name, None)
5065 if kwds:
5066 raise TypeError("Unknown arguments: %s." % kwds)
5068 # Special case: loc is fixed. Use the maximum likelihood formulas
5069 # instead of the numerical solver.
5071 if f0 is not None and fscale is not None:
5072 # This check is for consistency with `rv_continuous.fit`.
5073 raise ValueError("All parameters fixed. There is nothing to "
5074 "optimize.")
5076 data = np.asarray(data)
5078 if not np.isfinite(data).all():
5079 raise RuntimeError("The data contains non-finite values.")
5081 floc = float(floc)
5082 if floc != 0:
5083 # Shifting the data by floc. Don't do the subtraction in-place,
5084 # because `data` might be a view of the input array.
5085 data = data - floc
5086 if np.any(data <= 0):
5087 raise FitDataError("lognorm", lower=floc, upper=np.inf)
5088 lndata = np.log(data)
5090 # Three cases to handle:
5091 # * shape and scale both free
5092 # * shape fixed, scale free
5093 # * shape free, scale fixed
5095 if fscale is None:
5096 # scale is free.
5097 scale = np.exp(lndata.mean())
5098 if f0 is None:
5099 # shape is free.
5100 shape = lndata.std()
5101 else:
5102 # shape is fixed.
5103 shape = float(f0)
5104 else:
5105 # scale is fixed, shape is free
5106 scale = float(fscale)
5107 shape = np.sqrt(((lndata - np.log(scale))**2).mean())
5109 return shape, floc, scale
5112lognorm = lognorm_gen(a=0.0, name='lognorm')
5115class gilbrat_gen(rv_continuous):
5116 r"""A Gilbrat continuous random variable.
5118 %(before_notes)s
5120 Notes
5121 -----
5122 The probability density function for `gilbrat` is:
5124 .. math::
5126 f(x) = \frac{1}{x \sqrt{2\pi}} \exp(-\frac{1}{2} (\log(x))^2)
5128 `gilbrat` is a special case of `lognorm` with ``s=1``.
5130 %(after_notes)s
5132 %(example)s
5134 """
5135 _support_mask = rv_continuous._open_support_mask
5137 def _rvs(self, size=None, random_state=None):
5138 return np.exp(random_state.standard_normal(size))
5140 def _pdf(self, x):
5141 # gilbrat.pdf(x) = 1/(x*sqrt(2*pi)) * exp(-1/2*(log(x))**2)
5142 return np.exp(self._logpdf(x))
5144 def _logpdf(self, x):
5145 return _lognorm_logpdf(x, 1.0)
5147 def _cdf(self, x):
5148 return _norm_cdf(np.log(x))
5150 def _ppf(self, q):
5151 return np.exp(_norm_ppf(q))
5153 def _stats(self):
5154 p = np.e
5155 mu = np.sqrt(p)
5156 mu2 = p * (p - 1)
5157 g1 = np.sqrt((p - 1)) * (2 + p)
5158 g2 = np.polyval([1, 2, 3, 0, -6.0], p)
5159 return mu, mu2, g1, g2
5161 def _entropy(self):
5162 return 0.5 * np.log(2 * np.pi) + 0.5
5165gilbrat = gilbrat_gen(a=0.0, name='gilbrat')
5168class maxwell_gen(rv_continuous):
5169 r"""A Maxwell continuous random variable.
5171 %(before_notes)s
5173 Notes
5174 -----
5175 A special case of a `chi` distribution, with ``df=3``, ``loc=0.0``,
5176 and given ``scale = a``, where ``a`` is the parameter used in the
5177 Mathworld description [1]_.
5179 The probability density function for `maxwell` is:
5181 .. math::
5183 f(x) = \sqrt{2/\pi}x^2 \exp(-x^2/2)
5185 for :math:`x >= 0`.
5187 %(after_notes)s
5189 References
5190 ----------
5191 .. [1] http://mathworld.wolfram.com/MaxwellDistribution.html
5193 %(example)s
5194 """
5195 def _rvs(self, size=None, random_state=None):
5196 return chi.rvs(3.0, size=size, random_state=random_state)
5198 def _pdf(self, x):
5199 # maxwell.pdf(x) = sqrt(2/pi)x**2 * exp(-x**2/2)
5200 return _SQRT_2_OVER_PI*x*x*np.exp(-x*x/2.0)
5202 def _logpdf(self, x):
5203 return _LOG_SQRT_2_OVER_PI + 2*np.log(x) - 0.5*x*x
5205 def _cdf(self, x):
5206 return sc.gammainc(1.5, x*x/2.0)
5208 def _ppf(self, q):
5209 return np.sqrt(2*sc.gammaincinv(1.5, q))
5211 def _stats(self):
5212 val = 3*np.pi-8
5213 return (2*np.sqrt(2.0/np.pi),
5214 3-8/np.pi,
5215 np.sqrt(2)*(32-10*np.pi)/val**1.5,
5216 (-12*np.pi*np.pi + 160*np.pi - 384) / val**2.0)
5218 def _entropy(self):
5219 return _EULER + 0.5*np.log(2*np.pi)-0.5
5222maxwell = maxwell_gen(a=0.0, name='maxwell')
5225class mielke_gen(rv_continuous):
5226 r"""A Mielke Beta-Kappa / Dagum continuous random variable.
5228 %(before_notes)s
5230 Notes
5231 -----
5232 The probability density function for `mielke` is:
5234 .. math::
5236 f(x, k, s) = \frac{k x^{k-1}}{(1+x^s)^{1+k/s}}
5238 for :math:`x > 0` and :math:`k, s > 0`. The distribution is sometimes
5239 called Dagum distribution ([2]_). It was already defined in [3]_, called
5240 a Burr Type III distribution (`burr` with parameters ``c=s`` and
5241 ``d=k/s``).
5243 `mielke` takes ``k`` and ``s`` as shape parameters.
5245 %(after_notes)s
5247 References
5248 ----------
5249 .. [1] Mielke, P.W., 1973 "Another Family of Distributions for Describing
5250 and Analyzing Precipitation Data." J. Appl. Meteor., 12, 275-280
5251 .. [2] Dagum, C., 1977 "A new model for personal income distribution."
5252 Economie Appliquee, 33, 327-367.
5253 .. [3] Burr, I. W. "Cumulative frequency functions", Annals of
5254 Mathematical Statistics, 13(2), pp 215-232 (1942).
5256 %(example)s
5258 """
5259 def _argcheck(self, k, s):
5260 return (k > 0) & (s > 0)
5262 def _pdf(self, x, k, s):
5263 return k*x**(k-1.0) / (1.0+x**s)**(1.0+k*1.0/s)
5265 def _logpdf(self, x, k, s):
5266 return np.log(k) + np.log(x)*(k-1.0) - np.log1p(x**s)*(1.0+k*1.0/s)
5268 def _cdf(self, x, k, s):
5269 return x**k / (1.0+x**s)**(k*1.0/s)
5271 def _ppf(self, q, k, s):
5272 qsk = pow(q, s*1.0/k)
5273 return pow(qsk/(1.0-qsk), 1.0/s)
5275 def _munp(self, n, k, s):
5276 def nth_moment(n, k, s):
5277 # n-th moment is defined for -k < n < s
5278 return sc.gamma((k+n)/s)*sc.gamma(1-n/s)/sc.gamma(k/s)
5280 return _lazywhere(n < s, (n, k, s), nth_moment, np.inf)
5283mielke = mielke_gen(a=0.0, name='mielke')
5286class kappa4_gen(rv_continuous):
5287 r"""Kappa 4 parameter distribution.
5289 %(before_notes)s
5291 Notes
5292 -----
5293 The probability density function for kappa4 is:
5295 .. math::
5297 f(x, h, k) = (1 - k x)^{1/k - 1} (1 - h (1 - k x)^{1/k})^{1/h-1}
5299 if :math:`h` and :math:`k` are not equal to 0.
5301 If :math:`h` or :math:`k` are zero then the pdf can be simplified:
5303 h = 0 and k != 0::
5305 kappa4.pdf(x, h, k) = (1.0 - k*x)**(1.0/k - 1.0)*
5306 exp(-(1.0 - k*x)**(1.0/k))
5308 h != 0 and k = 0::
5310 kappa4.pdf(x, h, k) = exp(-x)*(1.0 - h*exp(-x))**(1.0/h - 1.0)
5312 h = 0 and k = 0::
5314 kappa4.pdf(x, h, k) = exp(-x)*exp(-exp(-x))
5316 kappa4 takes :math:`h` and :math:`k` as shape parameters.
5318 The kappa4 distribution returns other distributions when certain
5319 :math:`h` and :math:`k` values are used.
5321 +------+-------------+----------------+------------------+
5322 | h | k=0.0 | k=1.0 | -inf<=k<=inf |
5323 +======+=============+================+==================+
5324 | -1.0 | Logistic | | Generalized |
5325 | | | | Logistic(1) |
5326 | | | | |
5327 | | logistic(x) | | |
5328 +------+-------------+----------------+------------------+
5329 | 0.0 | Gumbel | Reverse | Generalized |
5330 | | | Exponential(2) | Extreme Value |
5331 | | | | |
5332 | | gumbel_r(x) | | genextreme(x, k) |
5333 +------+-------------+----------------+------------------+
5334 | 1.0 | Exponential | Uniform | Generalized |
5335 | | | | Pareto |
5336 | | | | |
5337 | | expon(x) | uniform(x) | genpareto(x, -k) |
5338 +------+-------------+----------------+------------------+
5340 (1) There are at least five generalized logistic distributions.
5341 Four are described here:
5342 https://en.wikipedia.org/wiki/Generalized_logistic_distribution
5343 The "fifth" one is the one kappa4 should match which currently
5344 isn't implemented in scipy:
5345 https://en.wikipedia.org/wiki/Talk:Generalized_logistic_distribution
5346 https://www.mathwave.com/help/easyfit/html/analyses/distributions/gen_logistic.html
5347 (2) This distribution is currently not in scipy.
5349 References
5350 ----------
5351 J.C. Finney, "Optimization of a Skewed Logistic Distribution With Respect
5352 to the Kolmogorov-Smirnov Test", A Dissertation Submitted to the Graduate
5353 Faculty of the Louisiana State University and Agricultural and Mechanical
5354 College, (August, 2004),
5355 https://digitalcommons.lsu.edu/gradschool_dissertations/3672
5357 J.R.M. Hosking, "The four-parameter kappa distribution". IBM J. Res.
5358 Develop. 38 (3), 25 1-258 (1994).
5360 B. Kumphon, A. Kaew-Man, P. Seenoi, "A Rainfall Distribution for the Lampao
5361 Site in the Chi River Basin, Thailand", Journal of Water Resource and
5362 Protection, vol. 4, 866-869, (2012).
5363 https://doi.org/10.4236/jwarp.2012.410101
5365 C. Winchester, "On Estimation of the Four-Parameter Kappa Distribution", A
5366 Thesis Submitted to Dalhousie University, Halifax, Nova Scotia, (March
5367 2000).
5368 http://www.nlc-bnc.ca/obj/s4/f2/dsk2/ftp01/MQ57336.pdf
5370 %(after_notes)s
5372 %(example)s
5374 """
5375 def _argcheck(self, h, k):
5376 return h == h
5378 def _get_support(self, h, k):
5379 condlist = [np.logical_and(h > 0, k > 0),
5380 np.logical_and(h > 0, k == 0),
5381 np.logical_and(h > 0, k < 0),
5382 np.logical_and(h <= 0, k > 0),
5383 np.logical_and(h <= 0, k == 0),
5384 np.logical_and(h <= 0, k < 0)]
5386 def f0(h, k):
5387 return (1.0 - float_power(h, -k))/k
5389 def f1(h, k):
5390 return np.log(h)
5392 def f3(h, k):
5393 a = np.empty(np.shape(h))
5394 a[:] = -np.inf
5395 return a
5397 def f5(h, k):
5398 return 1.0/k
5400 _a = _lazyselect(condlist,
5401 [f0, f1, f0, f3, f3, f5],
5402 [h, k],
5403 default=np.nan)
5405 def f0(h, k):
5406 return 1.0/k
5408 def f1(h, k):
5409 a = np.empty(np.shape(h))
5410 a[:] = np.inf
5411 return a
5413 _b = _lazyselect(condlist,
5414 [f0, f1, f1, f0, f1, f1],
5415 [h, k],
5416 default=np.nan)
5417 return _a, _b
5419 def _pdf(self, x, h, k):
5420 # kappa4.pdf(x, h, k) = (1.0 - k*x)**(1.0/k - 1.0)*
5421 # (1.0 - h*(1.0 - k*x)**(1.0/k))**(1.0/h-1)
5422 return np.exp(self._logpdf(x, h, k))
5424 def _logpdf(self, x, h, k):
5425 condlist = [np.logical_and(h != 0, k != 0),
5426 np.logical_and(h == 0, k != 0),
5427 np.logical_and(h != 0, k == 0),
5428 np.logical_and(h == 0, k == 0)]
5430 def f0(x, h, k):
5431 '''pdf = (1.0 - k*x)**(1.0/k - 1.0)*(
5432 1.0 - h*(1.0 - k*x)**(1.0/k))**(1.0/h-1.0)
5433 logpdf = ...
5434 '''
5435 return (sc.xlog1py(1.0/k - 1.0, -k*x) +
5436 sc.xlog1py(1.0/h - 1.0, -h*(1.0 - k*x)**(1.0/k)))
5438 def f1(x, h, k):
5439 '''pdf = (1.0 - k*x)**(1.0/k - 1.0)*np.exp(-(
5440 1.0 - k*x)**(1.0/k))
5441 logpdf = ...
5442 '''
5443 return sc.xlog1py(1.0/k - 1.0, -k*x) - (1.0 - k*x)**(1.0/k)
5445 def f2(x, h, k):
5446 '''pdf = np.exp(-x)*(1.0 - h*np.exp(-x))**(1.0/h - 1.0)
5447 logpdf = ...
5448 '''
5449 return -x + sc.xlog1py(1.0/h - 1.0, -h*np.exp(-x))
5451 def f3(x, h, k):
5452 '''pdf = np.exp(-x-np.exp(-x))
5453 logpdf = ...
5454 '''
5455 return -x - np.exp(-x)
5457 return _lazyselect(condlist,
5458 [f0, f1, f2, f3],
5459 [x, h, k],
5460 default=np.nan)
5462 def _cdf(self, x, h, k):
5463 return np.exp(self._logcdf(x, h, k))
5465 def _logcdf(self, x, h, k):
5466 condlist = [np.logical_and(h != 0, k != 0),
5467 np.logical_and(h == 0, k != 0),
5468 np.logical_and(h != 0, k == 0),
5469 np.logical_and(h == 0, k == 0)]
5471 def f0(x, h, k):
5472 '''cdf = (1.0 - h*(1.0 - k*x)**(1.0/k))**(1.0/h)
5473 logcdf = ...
5474 '''
5475 return (1.0/h)*sc.log1p(-h*(1.0 - k*x)**(1.0/k))
5477 def f1(x, h, k):
5478 '''cdf = np.exp(-(1.0 - k*x)**(1.0/k))
5479 logcdf = ...
5480 '''
5481 return -(1.0 - k*x)**(1.0/k)
5483 def f2(x, h, k):
5484 '''cdf = (1.0 - h*np.exp(-x))**(1.0/h)
5485 logcdf = ...
5486 '''
5487 return (1.0/h)*sc.log1p(-h*np.exp(-x))
5489 def f3(x, h, k):
5490 '''cdf = np.exp(-np.exp(-x))
5491 logcdf = ...
5492 '''
5493 return -np.exp(-x)
5495 return _lazyselect(condlist,
5496 [f0, f1, f2, f3],
5497 [x, h, k],
5498 default=np.nan)
5500 def _ppf(self, q, h, k):
5501 condlist = [np.logical_and(h != 0, k != 0),
5502 np.logical_and(h == 0, k != 0),
5503 np.logical_and(h != 0, k == 0),
5504 np.logical_and(h == 0, k == 0)]
5506 def f0(q, h, k):
5507 return 1.0/k*(1.0 - ((1.0 - (q**h))/h)**k)
5509 def f1(q, h, k):
5510 return 1.0/k*(1.0 - (-np.log(q))**k)
5512 def f2(q, h, k):
5513 '''ppf = -np.log((1.0 - (q**h))/h)
5514 '''
5515 return -sc.log1p(-(q**h)) + np.log(h)
5517 def f3(q, h, k):
5518 return -np.log(-np.log(q))
5520 return _lazyselect(condlist,
5521 [f0, f1, f2, f3],
5522 [q, h, k],
5523 default=np.nan)
5525 def _stats(self, h, k):
5526 if h >= 0 and k >= 0:
5527 maxr = 5
5528 elif h < 0 and k >= 0:
5529 maxr = int(-1.0/h*k)
5530 elif k < 0:
5531 maxr = int(-1.0/k)
5532 else:
5533 maxr = 5
5535 outputs = [None if r < maxr else np.nan for r in range(1, 5)]
5536 return outputs[:]
5539kappa4 = kappa4_gen(name='kappa4')
5542class kappa3_gen(rv_continuous):
5543 r"""Kappa 3 parameter distribution.
5545 %(before_notes)s
5547 Notes
5548 -----
5549 The probability density function for `kappa3` is:
5551 .. math::
5553 f(x, a) = a (a + x^a)^{-(a + 1)/a}
5555 for :math:`x > 0` and :math:`a > 0`.
5557 `kappa3` takes ``a`` as a shape parameter for :math:`a`.
5559 References
5560 ----------
5561 P.W. Mielke and E.S. Johnson, "Three-Parameter Kappa Distribution Maximum
5562 Likelihood and Likelihood Ratio Tests", Methods in Weather Research,
5563 701-707, (September, 1973),
5564 https://doi.org/10.1175/1520-0493(1973)101<0701:TKDMLE>2.3.CO;2
5566 B. Kumphon, "Maximum Entropy and Maximum Likelihood Estimation for the
5567 Three-Parameter Kappa Distribution", Open Journal of Statistics, vol 2,
5568 415-419 (2012), https://doi.org/10.4236/ojs.2012.24050
5570 %(after_notes)s
5572 %(example)s
5574 """
5575 def _argcheck(self, a):
5576 return a > 0
5578 def _pdf(self, x, a):
5579 # kappa3.pdf(x, a) = a*(a + x**a)**(-(a + 1)/a), for x > 0
5580 return a*(a + x**a)**(-1.0/a-1)
5582 def _cdf(self, x, a):
5583 return x*(a + x**a)**(-1.0/a)
5585 def _ppf(self, q, a):
5586 return (a/(q**-a - 1.0))**(1.0/a)
5588 def _stats(self, a):
5589 outputs = [None if i < a else np.nan for i in range(1, 5)]
5590 return outputs[:]
5593kappa3 = kappa3_gen(a=0.0, name='kappa3')
5595class moyal_gen(rv_continuous):
5596 r"""A Moyal continuous random variable.
5598 %(before_notes)s
5600 Notes
5601 -----
5602 The probability density function for `moyal` is:
5604 .. math::
5606 f(x) = \exp(-(x + \exp(-x))/2) / \sqrt{2\pi}
5608 for a real number :math:`x`.
5610 %(after_notes)s
5612 This distribution has utility in high-energy physics and radiation
5613 detection. It describes the energy loss of a charged relativistic
5614 particle due to ionization of the medium [1]_. It also provides an
5615 approximation for the Landau distribution. For an in depth description
5616 see [2]_. For additional description, see [3]_.
5618 References
5619 ----------
5620 .. [1] J.E. Moyal, "XXX. Theory of ionization fluctuations",
5621 The London, Edinburgh, and Dublin Philosophical Magazine
5622 and Journal of Science, vol 46, 263-280, (1955).
5623 :doi:`10.1080/14786440308521076` (gated)
5624 .. [2] G. Cordeiro et al., "The beta Moyal: a useful skew distribution",
5625 International Journal of Research and Reviews in Applied Sciences,
5626 vol 10, 171-192, (2012).
5627 http://www.arpapress.com/Volumes/Vol10Issue2/IJRRAS_10_2_02.pdf
5628 .. [3] C. Walck, "Handbook on Statistical Distributions for
5629 Experimentalists; International Report SUF-PFY/96-01", Chapter 26,
5630 University of Stockholm: Stockholm, Sweden, (2007).
5631 http://www.stat.rice.edu/~dobelman/textfiles/DistributionsHandbook.pdf
5633 .. versionadded:: 1.1.0
5635 %(example)s
5637 """
5638 def _rvs(self, size=None, random_state=None):
5639 u1 = gamma.rvs(a = 0.5, scale = 2, size=size, random_state=random_state)
5640 return -np.log(u1)
5642 def _pdf(self, x):
5643 return np.exp(-0.5 * (x + np.exp(-x))) / np.sqrt(2*np.pi)
5645 def _cdf(self, x):
5646 return sc.erfc(np.exp(-0.5 * x) / np.sqrt(2))
5648 def _sf(self, x):
5649 return sc.erf(np.exp(-0.5 * x) / np.sqrt(2))
5651 def _ppf(self, x):
5652 return -np.log(2 * sc.erfcinv(x)**2)
5654 def _stats(self):
5655 mu = np.log(2) + np.euler_gamma
5656 mu2 = np.pi**2 / 2
5657 g1 = 28 * np.sqrt(2) * sc.zeta(3) / np.pi**3
5658 g2 = 4.
5659 return mu, mu2, g1, g2
5661 def _munp(self, n):
5662 if n == 1.0:
5663 return np.log(2) + np.euler_gamma
5664 elif n == 2.0:
5665 return np.pi**2 / 2 + (np.log(2) + np.euler_gamma)**2
5666 elif n == 3.0:
5667 tmp1 = 1.5 * np.pi**2 * (np.log(2)+np.euler_gamma)
5668 tmp2 = (np.log(2)+np.euler_gamma)**3
5669 tmp3 = 14 * sc.zeta(3)
5670 return tmp1 + tmp2 + tmp3
5671 elif n == 4.0:
5672 tmp1 = 4 * 14 * sc.zeta(3) * (np.log(2) + np.euler_gamma)
5673 tmp2 = 3 * np.pi**2 * (np.log(2) + np.euler_gamma)**2
5674 tmp3 = (np.log(2) + np.euler_gamma)**4
5675 tmp4 = 7 * np.pi**4 / 4
5676 return tmp1 + tmp2 + tmp3 + tmp4
5677 else:
5678 # return generic for higher moments
5679 # return rv_continuous._mom1_sc(self, n, b)
5680 return self._mom1_sc(n)
5683moyal = moyal_gen(name="moyal")
5686class nakagami_gen(rv_continuous):
5687 r"""A Nakagami continuous random variable.
5689 %(before_notes)s
5691 Notes
5692 -----
5693 The probability density function for `nakagami` is:
5695 .. math::
5697 f(x, \nu) = \frac{2 \nu^\nu}{\Gamma(\nu)} x^{2\nu-1} \exp(-\nu x^2)
5699 for :math:`x >= 0`, :math:`\nu > 0`.
5701 `nakagami` takes ``nu`` as a shape parameter for :math:`\nu`.
5703 %(after_notes)s
5705 %(example)s
5707 """
5708 def _pdf(self, x, nu):
5709 # nakagami.pdf(x, nu) = 2 * nu**nu / gamma(nu) *
5710 # x**(2*nu-1) * exp(-nu*x**2)
5711 return 2*nu**nu/sc.gamma(nu)*(x**(2*nu-1.0))*np.exp(-nu*x*x)
5713 def _cdf(self, x, nu):
5714 return sc.gammainc(nu, nu*x*x)
5716 def _ppf(self, q, nu):
5717 return np.sqrt(1.0/nu*sc.gammaincinv(nu, q))
5719 def _stats(self, nu):
5720 mu = sc.gamma(nu+0.5)/sc.gamma(nu)/np.sqrt(nu)
5721 mu2 = 1.0-mu*mu
5722 g1 = mu * (1 - 4*nu*mu2) / 2.0 / nu / np.power(mu2, 1.5)
5723 g2 = -6*mu**4*nu + (8*nu-2)*mu**2-2*nu + 1
5724 g2 /= nu*mu2**2.0
5725 return mu, mu2, g1, g2
5728nakagami = nakagami_gen(a=0.0, name="nakagami")
5731class ncx2_gen(rv_continuous):
5732 r"""A non-central chi-squared continuous random variable.
5734 %(before_notes)s
5736 Notes
5737 -----
5738 The probability density function for `ncx2` is:
5740 .. math::
5742 f(x, k, \lambda) = \frac{1}{2} \exp(-(\lambda+x)/2)
5743 (x/\lambda)^{(k-2)/4} I_{(k-2)/2}(\sqrt{\lambda x})
5745 for :math:`x >= 0` and :math:`k, \lambda > 0`. :math:`k` specifies the
5746 degrees of freedom (denoted ``df`` in the implementation) and
5747 :math:`\lambda` is the non-centrality parameter (denoted ``nc`` in the
5748 implementation). :math:`I_\nu` denotes the modified Bessel function of
5749 first order of degree :math:`\nu` (`scipy.special.iv`).
5751 `ncx2` takes ``df`` and ``nc`` as shape parameters.
5753 %(after_notes)s
5755 %(example)s
5757 """
5758 def _argcheck(self, df, nc):
5759 return (df > 0) & (nc >= 0)
5761 def _rvs(self, df, nc, size=None, random_state=None):
5762 return random_state.noncentral_chisquare(df, nc, size)
5764 def _logpdf(self, x, df, nc):
5765 cond = np.ones_like(x, dtype=bool) & (nc != 0)
5766 return _lazywhere(cond, (x, df, nc), f=_ncx2_log_pdf, f2=chi2.logpdf)
5768 def _pdf(self, x, df, nc):
5769 # ncx2.pdf(x, df, nc) = exp(-(nc+x)/2) * 1/2 * (x/nc)**((df-2)/4)
5770 # * I[(df-2)/2](sqrt(nc*x))
5771 cond = np.ones_like(x, dtype=bool) & (nc != 0)
5772 return _lazywhere(cond, (x, df, nc), f=_ncx2_pdf, f2=chi2.pdf)
5774 def _cdf(self, x, df, nc):
5775 cond = np.ones_like(x, dtype=bool) & (nc != 0)
5776 return _lazywhere(cond, (x, df, nc), f=_ncx2_cdf, f2=chi2.cdf)
5778 def _ppf(self, q, df, nc):
5779 cond = np.ones_like(q, dtype=bool) & (nc != 0)
5780 return _lazywhere(cond, (q, df, nc), f=sc.chndtrix, f2=chi2.ppf)
5782 def _stats(self, df, nc):
5783 val = df + 2.0*nc
5784 return (df + nc,
5785 2*val,
5786 np.sqrt(8)*(val+nc)/val**1.5,
5787 12.0*(val+2*nc)/val**2.0)
5790ncx2 = ncx2_gen(a=0.0, name='ncx2')
5793class ncf_gen(rv_continuous):
5794 r"""A non-central F distribution continuous random variable.
5796 %(before_notes)s
5798 Notes
5799 -----
5800 The probability density function for `ncf` is:
5802 .. math::
5804 f(x, n_1, n_2, \lambda) =
5805 \exp\left(\frac{\lambda}{2} +
5806 \lambda n_1 \frac{x}{2(n_1 x + n_2)}
5807 \right)
5808 n_1^{n_1/2} n_2^{n_2/2} x^{n_1/2 - 1} \\
5809 (n_2 + n_1 x)^{-(n_1 + n_2)/2}
5810 \gamma(n_1/2) \gamma(1 + n_2/2) \\
5811 \frac{L^{\frac{n_1}{2}-1}_{n_2/2}
5812 \left(-\lambda n_1 \frac{x}{2(n_1 x + n_2)}\right)}
5813 {B(n_1/2, n_2/2)
5814 \gamma\left(\frac{n_1 + n_2}{2}\right)}
5816 for :math:`n_1, n_2 > 0`, :math:`\lambda\geq 0`. Here :math:`n_1` is the
5817 degrees of freedom in the numerator, :math:`n_2` the degrees of freedom in
5818 the denominator, :math:`\lambda` the non-centrality parameter,
5819 :math:`\gamma` is the logarithm of the Gamma function, :math:`L_n^k` is a
5820 generalized Laguerre polynomial and :math:`B` is the beta function.
5822 `ncf` takes ``df1``, ``df2`` and ``nc`` as shape parameters. If ``nc=0``,
5823 the distribution becomes equivalent to the Fisher distribution.
5825 %(after_notes)s
5827 See Also
5828 --------
5829 scipy.stats.f : Fisher distribution
5831 %(example)s
5833 """
5834 def _argcheck(self, df1, df2, nc):
5835 return (df1 > 0) & (df2 > 0) & (nc >= 0)
5837 def _rvs(self, dfn, dfd, nc, size=None, random_state=None):
5838 return random_state.noncentral_f(dfn, dfd, nc, size)
5840 def _pdf_skip(self, x, dfn, dfd, nc):
5841 # ncf.pdf(x, df1, df2, nc) = exp(nc/2 + nc*df1*x/(2*(df1*x+df2))) *
5842 # df1**(df1/2) * df2**(df2/2) * x**(df1/2-1) *
5843 # (df2+df1*x)**(-(df1+df2)/2) *
5844 # gamma(df1/2)*gamma(1+df2/2) *
5845 # L^{v1/2-1}^{v2/2}(-nc*v1*x/(2*(v1*x+v2))) /
5846 # (B(v1/2, v2/2) * gamma((v1+v2)/2))
5847 n1, n2 = dfn, dfd
5848 term = -nc/2+nc*n1*x/(2*(n2+n1*x)) + sc.gammaln(n1/2.)+sc.gammaln(1+n2/2.)
5849 term -= sc.gammaln((n1+n2)/2.0)
5850 Px = np.exp(term)
5851 Px *= n1**(n1/2) * n2**(n2/2) * x**(n1/2-1)
5852 Px *= (n2+n1*x)**(-(n1+n2)/2)
5853 Px *= sc.assoc_laguerre(-nc*n1*x/(2.0*(n2+n1*x)), n2/2, n1/2-1)
5854 Px /= sc.beta(n1/2, n2/2)
5855 # This function does not have a return. Drop it for now, the generic
5856 # function seems to work OK.
5858 def _cdf(self, x, dfn, dfd, nc):
5859 return sc.ncfdtr(dfn, dfd, nc, x)
5861 def _ppf(self, q, dfn, dfd, nc):
5862 return sc.ncfdtri(dfn, dfd, nc, q)
5864 def _munp(self, n, dfn, dfd, nc):
5865 val = (dfn * 1.0/dfd)**n
5866 term = sc.gammaln(n+0.5*dfn) + sc.gammaln(0.5*dfd-n) - sc.gammaln(dfd*0.5)
5867 val *= np.exp(-nc / 2.0+term)
5868 val *= sc.hyp1f1(n+0.5*dfn, 0.5*dfn, 0.5*nc)
5869 return val
5871 def _stats(self, dfn, dfd, nc):
5872 # Note: the rv_continuous class ensures that dfn > 0 when this function
5873 # is called, so we don't have to check for division by zero with dfn
5874 # in the following.
5875 mu_num = dfd * (dfn + nc)
5876 mu_den = dfn * (dfd - 2)
5877 mu = np.full_like(mu_num, dtype=np.float64, fill_value=np.inf)
5878 np.true_divide(mu_num, mu_den, where=dfd > 2, out=mu)
5880 mu2_num = 2*((dfn + nc)**2 + (dfn + 2*nc)*(dfd - 2))*(dfd/dfn)**2
5881 mu2_den = (dfd - 2)**2 * (dfd - 4)
5882 mu2 = np.full_like(mu2_num, dtype=np.float64, fill_value=np.inf)
5883 np.true_divide(mu2_num, mu2_den, where=dfd > 4, out=mu2)
5885 return mu, mu2, None, None
5888ncf = ncf_gen(a=0.0, name='ncf')
5891class t_gen(rv_continuous):
5892 r"""A Student's t continuous random variable.
5894 %(before_notes)s
5896 Notes
5897 -----
5898 The probability density function for `t` is:
5900 .. math::
5902 f(x, \nu) = \frac{\Gamma((\nu+1)/2)}
5903 {\sqrt{\pi \nu} \Gamma(\nu/2)}
5904 (1+x^2/\nu)^{-(\nu+1)/2}
5906 where :math:`x` is a real number and the degrees of freedom parameter
5907 :math:`\nu` (denoted ``df`` in the implementation) satisfies
5908 :math:`\nu > 0`. :math:`\Gamma` is the gamma function
5909 (`scipy.special.gamma`).
5911 %(after_notes)s
5913 %(example)s
5915 """
5916 def _argcheck(self, df):
5917 return df > 0
5919 def _rvs(self, df, size=None, random_state=None):
5920 return random_state.standard_t(df, size=size)
5922 def _pdf(self, x, df):
5923 # gamma((df+1)/2)
5924 # t.pdf(x, df) = ---------------------------------------------------
5925 # sqrt(pi*df) * gamma(df/2) * (1+x**2/df)**((df+1)/2)
5926 r = np.asarray(df*1.0)
5927 Px = np.exp(sc.gammaln((r+1)/2)-sc.gammaln(r/2))
5928 Px /= np.sqrt(r*np.pi)*(1+(x**2)/r)**((r+1)/2)
5929 return Px
5931 def _logpdf(self, x, df):
5932 r = df*1.0
5933 lPx = sc.gammaln((r+1)/2)-sc.gammaln(r/2)
5934 lPx -= 0.5*np.log(r*np.pi) + (r+1)/2*np.log(1+(x**2)/r)
5935 return lPx
5937 def _cdf(self, x, df):
5938 return sc.stdtr(df, x)
5940 def _sf(self, x, df):
5941 return sc.stdtr(df, -x)
5943 def _ppf(self, q, df):
5944 return sc.stdtrit(df, q)
5946 def _isf(self, q, df):
5947 return -sc.stdtrit(df, q)
5949 def _stats(self, df):
5950 mu = np.where(df > 1, 0.0, np.inf)
5951 mu2 = _lazywhere(df > 2, (df,),
5952 lambda df: df / (df-2.0),
5953 np.inf)
5954 mu2 = np.where(df <= 1, np.nan, mu2)
5955 g1 = np.where(df > 3, 0.0, np.nan)
5956 g2 = _lazywhere(df > 4, (df,),
5957 lambda df: 6.0 / (df-4.0),
5958 np.inf)
5959 g2 = np.where(df <= 2, np.nan, g2)
5960 return mu, mu2, g1, g2
5963t = t_gen(name='t')
5966class nct_gen(rv_continuous):
5967 r"""A non-central Student's t continuous random variable.
5969 %(before_notes)s
5971 Notes
5972 -----
5973 If :math:`Y` is a standard normal random variable and :math:`V` is
5974 an independent chi-square random variable (`chi2`) with :math:`k` degrees
5975 of freedom, then
5977 .. math::
5979 X = \frac{Y + c}{\sqrt{V/k}}
5981 has a non-central Student's t distribution on the real line.
5982 The degrees of freedom parameter :math:`k` (denoted ``df`` in the
5983 implementation) satisfies :math:`k > 0` and the noncentrality parameter
5984 :math:`c` (denoted ``nc`` in the implementation) is a real number.
5986 %(after_notes)s
5988 %(example)s
5990 """
5991 def _argcheck(self, df, nc):
5992 return (df > 0) & (nc == nc)
5994 def _rvs(self, df, nc, size=None, random_state=None):
5995 n = norm.rvs(loc=nc, size=size, random_state=random_state)
5996 c2 = chi2.rvs(df, size=size, random_state=random_state)
5997 return n * np.sqrt(df) / np.sqrt(c2)
5999 def _pdf(self, x, df, nc):
6000 n = df*1.0
6001 nc = nc*1.0
6002 x2 = x*x
6003 ncx2 = nc*nc*x2
6004 fac1 = n + x2
6005 trm1 = n/2.*np.log(n) + sc.gammaln(n+1)
6006 trm1 -= n*np.log(2)+nc*nc/2.+(n/2.)*np.log(fac1)+sc.gammaln(n/2.)
6007 Px = np.exp(trm1)
6008 valF = ncx2 / (2*fac1)
6009 trm1 = np.sqrt(2)*nc*x*sc.hyp1f1(n/2+1, 1.5, valF)
6010 trm1 /= np.asarray(fac1*sc.gamma((n+1)/2))
6011 trm2 = sc.hyp1f1((n+1)/2, 0.5, valF)
6012 trm2 /= np.asarray(np.sqrt(fac1)*sc.gamma(n/2+1))
6013 Px *= trm1+trm2
6014 return Px
6016 def _cdf(self, x, df, nc):
6017 return sc.nctdtr(df, nc, x)
6019 def _ppf(self, q, df, nc):
6020 return sc.nctdtrit(df, nc, q)
6022 def _stats(self, df, nc, moments='mv'):
6023 #
6024 # See D. Hogben, R.S. Pinkham, and M.B. Wilk,
6025 # 'The moments of the non-central t-distribution'
6026 # Biometrika 48, p. 465 (2961).
6027 # e.g. https://www.jstor.org/stable/2332772 (gated)
6028 #
6029 mu, mu2, g1, g2 = None, None, None, None
6031 gfac = sc.gamma(df/2.-0.5) / sc.gamma(df/2.)
6032 c11 = np.sqrt(df/2.) * gfac
6033 c20 = df / (df-2.)
6034 c22 = c20 - c11*c11
6035 mu = np.where(df > 1, nc*c11, np.inf)
6036 mu2 = np.where(df > 2, c22*nc*nc + c20, np.inf)
6037 if 's' in moments:
6038 c33t = df * (7.-2.*df) / (df-2.) / (df-3.) + 2.*c11*c11
6039 c31t = 3.*df / (df-2.) / (df-3.)
6040 mu3 = (c33t*nc*nc + c31t) * c11*nc
6041 g1 = np.where(df > 3, mu3 / np.power(mu2, 1.5), np.nan)
6042 # kurtosis
6043 if 'k' in moments:
6044 c44 = df*df / (df-2.) / (df-4.)
6045 c44 -= c11*c11 * 2.*df*(5.-df) / (df-2.) / (df-3.)
6046 c44 -= 3.*c11**4
6047 c42 = df / (df-4.) - c11*c11 * (df-1.) / (df-3.)
6048 c42 *= 6.*df / (df-2.)
6049 c40 = 3.*df*df / (df-2.) / (df-4.)
6051 mu4 = c44 * nc**4 + c42*nc**2 + c40
6052 g2 = np.where(df > 4, mu4/mu2**2 - 3., np.nan)
6053 return mu, mu2, g1, g2
6056nct = nct_gen(name="nct")
6059class pareto_gen(rv_continuous):
6060 r"""A Pareto continuous random variable.
6062 %(before_notes)s
6064 Notes
6065 -----
6066 The probability density function for `pareto` is:
6068 .. math::
6070 f(x, b) = \frac{b}{x^{b+1}}
6072 for :math:`x \ge 1`, :math:`b > 0`.
6074 `pareto` takes ``b`` as a shape parameter for :math:`b`.
6076 %(after_notes)s
6078 %(example)s
6080 """
6081 def _pdf(self, x, b):
6082 # pareto.pdf(x, b) = b / x**(b+1)
6083 return b * x**(-b-1)
6085 def _cdf(self, x, b):
6086 return 1 - x**(-b)
6088 def _ppf(self, q, b):
6089 return pow(1-q, -1.0/b)
6091 def _sf(self, x, b):
6092 return x**(-b)
6094 def _stats(self, b, moments='mv'):
6095 mu, mu2, g1, g2 = None, None, None, None
6096 if 'm' in moments:
6097 mask = b > 1
6098 bt = np.extract(mask, b)
6099 mu = valarray(np.shape(b), value=np.inf)
6100 np.place(mu, mask, bt / (bt-1.0))
6101 if 'v' in moments:
6102 mask = b > 2
6103 bt = np.extract(mask, b)
6104 mu2 = valarray(np.shape(b), value=np.inf)
6105 np.place(mu2, mask, bt / (bt-2.0) / (bt-1.0)**2)
6106 if 's' in moments:
6107 mask = b > 3
6108 bt = np.extract(mask, b)
6109 g1 = valarray(np.shape(b), value=np.nan)
6110 vals = 2 * (bt + 1.0) * np.sqrt(bt - 2.0) / ((bt - 3.0) * np.sqrt(bt))
6111 np.place(g1, mask, vals)
6112 if 'k' in moments:
6113 mask = b > 4
6114 bt = np.extract(mask, b)
6115 g2 = valarray(np.shape(b), value=np.nan)
6116 vals = (6.0*np.polyval([1.0, 1.0, -6, -2], bt) /
6117 np.polyval([1.0, -7.0, 12.0, 0.0], bt))
6118 np.place(g2, mask, vals)
6119 return mu, mu2, g1, g2
6121 def _entropy(self, c):
6122 return 1 + 1.0/c - np.log(c)
6125pareto = pareto_gen(a=1.0, name="pareto")
6128class lomax_gen(rv_continuous):
6129 r"""A Lomax (Pareto of the second kind) continuous random variable.
6131 %(before_notes)s
6133 Notes
6134 -----
6135 The probability density function for `lomax` is:
6137 .. math::
6139 f(x, c) = \frac{c}{(1+x)^{c+1}}
6141 for :math:`x \ge 0`, :math:`c > 0`.
6143 `lomax` takes ``c`` as a shape parameter for :math:`c`.
6145 `lomax` is a special case of `pareto` with ``loc=-1.0``.
6147 %(after_notes)s
6149 %(example)s
6151 """
6152 def _pdf(self, x, c):
6153 # lomax.pdf(x, c) = c / (1+x)**(c+1)
6154 return c*1.0/(1.0+x)**(c+1.0)
6156 def _logpdf(self, x, c):
6157 return np.log(c) - (c+1)*sc.log1p(x)
6159 def _cdf(self, x, c):
6160 return -sc.expm1(-c*sc.log1p(x))
6162 def _sf(self, x, c):
6163 return np.exp(-c*sc.log1p(x))
6165 def _logsf(self, x, c):
6166 return -c*sc.log1p(x)
6168 def _ppf(self, q, c):
6169 return sc.expm1(-sc.log1p(-q)/c)
6171 def _stats(self, c):
6172 mu, mu2, g1, g2 = pareto.stats(c, loc=-1.0, moments='mvsk')
6173 return mu, mu2, g1, g2
6175 def _entropy(self, c):
6176 return 1+1.0/c-np.log(c)
6179lomax = lomax_gen(a=0.0, name="lomax")
6182class pearson3_gen(rv_continuous):
6183 r"""A pearson type III continuous random variable.
6185 %(before_notes)s
6187 Notes
6188 -----
6189 The probability density function for `pearson3` is:
6191 .. math::
6193 f(x, skew) = \frac{|\beta|}{\Gamma(\alpha)}
6194 (\beta (x - \zeta))^{\alpha - 1}
6195 \exp(-\beta (x - \zeta))
6197 where:
6199 .. math::
6201 \beta = \frac{2}{skew stddev}
6202 \alpha = (stddev \beta)^2
6203 \zeta = loc - \frac{\alpha}{\beta}
6205 :math:`\Gamma` is the gamma function (`scipy.special.gamma`).
6206 `pearson3` takes ``skew`` as a shape parameter for :math:`skew`.
6208 %(after_notes)s
6210 %(example)s
6212 References
6213 ----------
6214 R.W. Vogel and D.E. McMartin, "Probability Plot Goodness-of-Fit and
6215 Skewness Estimation Procedures for the Pearson Type 3 Distribution", Water
6216 Resources Research, Vol.27, 3149-3158 (1991).
6218 L.R. Salvosa, "Tables of Pearson's Type III Function", Ann. Math. Statist.,
6219 Vol.1, 191-198 (1930).
6221 "Using Modern Computing Tools to Fit the Pearson Type III Distribution to
6222 Aviation Loads Data", Office of Aviation Research (2003).
6224 """
6225 def _preprocess(self, x, skew):
6226 # The real 'loc' and 'scale' are handled in the calling pdf(...). The
6227 # local variables 'loc' and 'scale' within pearson3._pdf are set to
6228 # the defaults just to keep them as part of the equations for
6229 # documentation.
6230 loc = 0.0
6231 scale = 1.0
6233 # If skew is small, return _norm_pdf. The divide between pearson3
6234 # and norm was found by brute force and is approximately a skew of
6235 # 0.000016. No one, I hope, would actually use a skew value even
6236 # close to this small.
6237 norm2pearson_transition = 0.000016
6239 ans, x, skew = np.broadcast_arrays([1.0], x, skew)
6240 ans = ans.copy()
6242 # mask is True where skew is small enough to use the normal approx.
6243 mask = np.absolute(skew) < norm2pearson_transition
6244 invmask = ~mask
6246 beta = 2.0 / (skew[invmask] * scale)
6247 alpha = (scale * beta)**2
6248 zeta = loc - alpha / beta
6250 transx = beta * (x[invmask] - zeta)
6251 return ans, x, transx, mask, invmask, beta, alpha, zeta
6253 def _argcheck(self, skew):
6254 # The _argcheck function in rv_continuous only allows positive
6255 # arguments. The skew argument for pearson3 can be zero (which I want
6256 # to handle inside pearson3._pdf) or negative. So just return True
6257 # for all skew args.
6258 return np.ones(np.shape(skew), dtype=bool)
6260 def _stats(self, skew):
6261 _, _, _, _, _, beta, alpha, zeta = (
6262 self._preprocess([1], skew))
6263 m = zeta + alpha / beta
6264 v = alpha / (beta**2)
6265 s = 2.0 / (alpha**0.5) * np.sign(beta)
6266 k = 6.0 / alpha
6267 return m, v, s, k
6269 def _pdf(self, x, skew):
6270 # pearson3.pdf(x, skew) = abs(beta) / gamma(alpha) *
6271 # (beta * (x - zeta))**(alpha - 1) * exp(-beta*(x - zeta))
6272 # Do the calculation in _logpdf since helps to limit
6273 # overflow/underflow problems
6274 ans = np.exp(self._logpdf(x, skew))
6275 if ans.ndim == 0:
6276 if np.isnan(ans):
6277 return 0.0
6278 return ans
6279 ans[np.isnan(ans)] = 0.0
6280 return ans
6282 def _logpdf(self, x, skew):
6283 # PEARSON3 logpdf GAMMA logpdf
6284 # np.log(abs(beta))
6285 # + (alpha - 1)*np.log(beta*(x - zeta)) + (a - 1)*np.log(x)
6286 # - beta*(x - zeta) - x
6287 # - sc.gammalnalpha) - sc.gammalna)
6288 ans, x, transx, mask, invmask, beta, alpha, _ = (
6289 self._preprocess(x, skew))
6291 ans[mask] = np.log(_norm_pdf(x[mask]))
6292 ans[invmask] = np.log(abs(beta)) + gamma._logpdf(transx, alpha)
6293 return ans
6295 def _cdf(self, x, skew):
6296 ans, x, transx, mask, invmask, _, alpha, _ = (
6297 self._preprocess(x, skew))
6299 ans[mask] = _norm_cdf(x[mask])
6300 ans[invmask] = gamma._cdf(transx, alpha)
6301 return ans
6303 def _rvs(self, skew, size=None, random_state=None):
6304 skew = np.broadcast_to(skew, size)
6305 ans, _, _, mask, invmask, beta, alpha, zeta = (
6306 self._preprocess([0], skew))
6308 nsmall = mask.sum()
6309 nbig = mask.size - nsmall
6310 ans[mask] = random_state.standard_normal(nsmall)
6311 ans[invmask] = random_state.standard_gamma(alpha, nbig)/beta + zeta
6313 if size == ():
6314 ans = ans[0]
6315 return ans
6317 def _ppf(self, q, skew):
6318 ans, q, _, mask, invmask, beta, alpha, zeta = (
6319 self._preprocess(q, skew))
6320 ans[mask] = _norm_ppf(q[mask])
6321 ans[invmask] = sc.gammaincinv(alpha, q[invmask])/beta + zeta
6322 return ans
6325pearson3 = pearson3_gen(name="pearson3")
6328class powerlaw_gen(rv_continuous):
6329 r"""A power-function continuous random variable.
6331 %(before_notes)s
6333 Notes
6334 -----
6335 The probability density function for `powerlaw` is:
6337 .. math::
6339 f(x, a) = a x^{a-1}
6341 for :math:`0 \le x \le 1`, :math:`a > 0`.
6343 `powerlaw` takes ``a`` as a shape parameter for :math:`a`.
6345 %(after_notes)s
6347 `powerlaw` is a special case of `beta` with ``b=1``.
6349 %(example)s
6351 """
6352 def _pdf(self, x, a):
6353 # powerlaw.pdf(x, a) = a * x**(a-1)
6354 return a*x**(a-1.0)
6356 def _logpdf(self, x, a):
6357 return np.log(a) + sc.xlogy(a - 1, x)
6359 def _cdf(self, x, a):
6360 return x**(a*1.0)
6362 def _logcdf(self, x, a):
6363 return a*np.log(x)
6365 def _ppf(self, q, a):
6366 return pow(q, 1.0/a)
6368 def _stats(self, a):
6369 return (a / (a + 1.0),
6370 a / (a + 2.0) / (a + 1.0) ** 2,
6371 -2.0 * ((a - 1.0) / (a + 3.0)) * np.sqrt((a + 2.0) / a),
6372 6 * np.polyval([1, -1, -6, 2], a) / (a * (a + 3.0) * (a + 4)))
6374 def _entropy(self, a):
6375 return 1 - 1.0/a - np.log(a)
6378powerlaw = powerlaw_gen(a=0.0, b=1.0, name="powerlaw")
6381class powerlognorm_gen(rv_continuous):
6382 r"""A power log-normal continuous random variable.
6384 %(before_notes)s
6386 Notes
6387 -----
6388 The probability density function for `powerlognorm` is:
6390 .. math::
6392 f(x, c, s) = \frac{c}{x s} \phi(\log(x)/s)
6393 (\Phi(-\log(x)/s))^{c-1}
6395 where :math:`\phi` is the normal pdf, and :math:`\Phi` is the normal cdf,
6396 and :math:`x > 0`, :math:`s, c > 0`.
6398 `powerlognorm` takes :math:`c` and :math:`s` as shape parameters.
6400 %(after_notes)s
6402 %(example)s
6404 """
6405 _support_mask = rv_continuous._open_support_mask
6407 def _pdf(self, x, c, s):
6408 # powerlognorm.pdf(x, c, s) = c / (x*s) * phi(log(x)/s) *
6409 # (Phi(-log(x)/s))**(c-1),
6410 return (c/(x*s) * _norm_pdf(np.log(x)/s) *
6411 pow(_norm_cdf(-np.log(x)/s), c*1.0-1.0))
6413 def _cdf(self, x, c, s):
6414 return 1.0 - pow(_norm_cdf(-np.log(x)/s), c*1.0)
6416 def _ppf(self, q, c, s):
6417 return np.exp(-s * _norm_ppf(pow(1.0 - q, 1.0 / c)))
6420powerlognorm = powerlognorm_gen(a=0.0, name="powerlognorm")
6423class powernorm_gen(rv_continuous):
6424 r"""A power normal continuous random variable.
6426 %(before_notes)s
6428 Notes
6429 -----
6430 The probability density function for `powernorm` is:
6432 .. math::
6434 f(x, c) = c \phi(x) (\Phi(-x))^{c-1}
6436 where :math:`\phi` is the normal pdf, and :math:`\Phi` is the normal cdf,
6437 and :math:`x >= 0`, :math:`c > 0`.
6439 `powernorm` takes ``c`` as a shape parameter for :math:`c`.
6441 %(after_notes)s
6443 %(example)s
6445 """
6446 def _pdf(self, x, c):
6447 # powernorm.pdf(x, c) = c * phi(x) * (Phi(-x))**(c-1)
6448 return c*_norm_pdf(x) * (_norm_cdf(-x)**(c-1.0))
6450 def _logpdf(self, x, c):
6451 return np.log(c) + _norm_logpdf(x) + (c-1)*_norm_logcdf(-x)
6453 def _cdf(self, x, c):
6454 return 1.0-_norm_cdf(-x)**(c*1.0)
6456 def _ppf(self, q, c):
6457 return -_norm_ppf(pow(1.0 - q, 1.0 / c))
6460powernorm = powernorm_gen(name='powernorm')
6463class rdist_gen(rv_continuous):
6464 r"""An R-distributed (symmetric beta) continuous random variable.
6466 %(before_notes)s
6468 Notes
6469 -----
6470 The probability density function for `rdist` is:
6472 .. math::
6474 f(x, c) = \frac{(1-x^2)^{c/2-1}}{B(1/2, c/2)}
6476 for :math:`-1 \le x \le 1`, :math:`c > 0`. `rdist` is also called the
6477 symmetric beta distribution: if B has a `beta` distribution with
6478 parameters (c/2, c/2), then X = 2*B - 1 follows a R-distribution with
6479 parameter c.
6481 `rdist` takes ``c`` as a shape parameter for :math:`c`.
6483 This distribution includes the following distribution kernels as
6484 special cases::
6486 c = 2: uniform
6487 c = 3: `semicircular`
6488 c = 4: Epanechnikov (parabolic)
6489 c = 6: quartic (biweight)
6490 c = 8: triweight
6492 %(after_notes)s
6494 %(example)s
6496 """
6497 # use relation to the beta distribution for pdf, cdf, etc
6498 def _pdf(self, x, c):
6499 return 0.5*beta._pdf((x + 1)/2, c/2, c/2)
6501 def _logpdf(self, x, c):
6502 return -np.log(2) + beta._logpdf((x + 1)/2, c/2, c/2)
6504 def _cdf(self, x, c):
6505 return beta._cdf((x + 1)/2, c/2, c/2)
6507 def _ppf(self, q, c):
6508 return 2*beta._ppf(q, c/2, c/2) - 1
6510 def _rvs(self, c, size=None, random_state=None):
6511 return 2 * random_state.beta(c/2, c/2, size) - 1
6513 def _munp(self, n, c):
6514 numerator = (1 - (n % 2)) * sc.beta((n + 1.0) / 2, c / 2.0)
6515 return numerator / sc.beta(1. / 2, c / 2.)
6518rdist = rdist_gen(a=-1.0, b=1.0, name="rdist")
6521class rayleigh_gen(rv_continuous):
6522 r"""A Rayleigh continuous random variable.
6524 %(before_notes)s
6526 Notes
6527 -----
6528 The probability density function for `rayleigh` is:
6530 .. math::
6532 f(x) = x \exp(-x^2/2)
6534 for :math:`x \ge 0`.
6536 `rayleigh` is a special case of `chi` with ``df=2``.
6538 %(after_notes)s
6540 %(example)s
6542 """
6543 _support_mask = rv_continuous._open_support_mask
6545 def _rvs(self, size=None, random_state=None):
6546 return chi.rvs(2, size=size, random_state=random_state)
6548 def _pdf(self, r):
6549 # rayleigh.pdf(r) = r * exp(-r**2/2)
6550 return np.exp(self._logpdf(r))
6552 def _logpdf(self, r):
6553 return np.log(r) - 0.5 * r * r
6555 def _cdf(self, r):
6556 return -sc.expm1(-0.5 * r**2)
6558 def _ppf(self, q):
6559 return np.sqrt(-2 * sc.log1p(-q))
6561 def _sf(self, r):
6562 return np.exp(self._logsf(r))
6564 def _logsf(self, r):
6565 return -0.5 * r * r
6567 def _isf(self, q):
6568 return np.sqrt(-2 * np.log(q))
6570 def _stats(self):
6571 val = 4 - np.pi
6572 return (np.sqrt(np.pi/2),
6573 val/2,
6574 2*(np.pi-3)*np.sqrt(np.pi)/val**1.5,
6575 6*np.pi/val-16/val**2)
6577 def _entropy(self):
6578 return _EULER/2.0 + 1 - 0.5*np.log(2)
6581rayleigh = rayleigh_gen(a=0.0, name="rayleigh")
6584class reciprocal_gen(rv_continuous):
6585 r"""A loguniform or reciprocal continuous random variable.
6587 %(before_notes)s
6589 Notes
6590 -----
6591 The probability density function for this class is:
6593 .. math::
6595 f(x, a, b) = \frac{1}{x \log(b/a)}
6597 for :math:`a \le x \le b`, :math:`b > a > 0`. This class takes
6598 :math:`a` and :math:`b` as shape parameters. %(after_notes)s
6600 %(example)s
6602 This doesn't show the equal probability of ``0.01``, ``0.1`` and
6603 ``1``. This is best when the x-axis is log-scaled:
6605 >>> import numpy as np
6606 >>> fig, ax = plt.subplots(1, 1)
6607 >>> ax.hist(np.log10(r))
6608 >>> ax.set_ylabel("Frequency")
6609 >>> ax.set_xlabel("Value of random variable")
6610 >>> ax.xaxis.set_major_locator(plt.FixedLocator([-2, -1, 0]))
6611 >>> ticks = ["$10^{{ {} }}$".format(i) for i in [-2, -1, 0]]
6612 >>> ax.set_xticklabels(ticks) # doctest: +SKIP
6613 >>> plt.show()
6615 This random variable will be log-uniform regardless of the base chosen for
6616 ``a`` and ``b``. Let's specify with base ``2`` instead:
6618 >>> rvs = %(name)s(2**-2, 2**0).rvs(size=1000)
6620 Values of ``1/4``, ``1/2`` and ``1`` are equally likely with this random
6621 variable. Here's the histogram:
6623 >>> fig, ax = plt.subplots(1, 1)
6624 >>> ax.hist(np.log2(rvs))
6625 >>> ax.set_ylabel("Frequency")
6626 >>> ax.set_xlabel("Value of random variable")
6627 >>> ax.xaxis.set_major_locator(plt.FixedLocator([-2, -1, 0]))
6628 >>> ticks = ["$2^{{ {} }}$".format(i) for i in [-2, -1, 0]]
6629 >>> ax.set_xticklabels(ticks) # doctest: +SKIP
6630 >>> plt.show()
6632 """
6633 def _argcheck(self, a, b):
6634 return (a > 0) & (b > a)
6636 def _get_support(self, a, b):
6637 return a, b
6639 def _pdf(self, x, a, b):
6640 # reciprocal.pdf(x, a, b) = 1 / (x*log(b/a))
6641 return 1.0 / (x * np.log(b * 1.0 / a))
6643 def _logpdf(self, x, a, b):
6644 return -np.log(x) - np.log(np.log(b * 1.0 / a))
6646 def _cdf(self, x, a, b):
6647 return (np.log(x)-np.log(a)) / np.log(b * 1.0 / a)
6649 def _ppf(self, q, a, b):
6650 return a*pow(b*1.0/a, q)
6652 def _munp(self, n, a, b):
6653 return 1.0/np.log(b*1.0/a) / n * (pow(b*1.0, n) - pow(a*1.0, n))
6655 def _entropy(self, a, b):
6656 return 0.5*np.log(a*b)+np.log(np.log(b*1.0/a))
6659loguniform = reciprocal_gen(name="loguniform")
6660reciprocal = reciprocal_gen(name="reciprocal")
6663class rice_gen(rv_continuous):
6664 r"""A Rice continuous random variable.
6666 %(before_notes)s
6668 Notes
6669 -----
6670 The probability density function for `rice` is:
6672 .. math::
6674 f(x, b) = x \exp(- \frac{x^2 + b^2}{2}) I_0(x b)
6676 for :math:`x >= 0`, :math:`b > 0`. :math:`I_0` is the modified Bessel
6677 function of order zero (`scipy.special.i0`).
6679 `rice` takes ``b`` as a shape parameter for :math:`b`.
6681 %(after_notes)s
6683 The Rice distribution describes the length, :math:`r`, of a 2-D vector with
6684 components :math:`(U+u, V+v)`, where :math:`U, V` are constant, :math:`u,
6685 v` are independent Gaussian random variables with standard deviation
6686 :math:`s`. Let :math:`R = \sqrt{U^2 + V^2}`. Then the pdf of :math:`r` is
6687 ``rice.pdf(x, R/s, scale=s)``.
6689 %(example)s
6691 """
6692 def _argcheck(self, b):
6693 return b >= 0
6695 def _rvs(self, b, size=None, random_state=None):
6696 # https://en.wikipedia.org/wiki/Rice_distribution
6697 t = b/np.sqrt(2) + random_state.standard_normal(size=(2,) + size)
6698 return np.sqrt((t*t).sum(axis=0))
6700 def _cdf(self, x, b):
6701 return sc.chndtr(np.square(x), 2, np.square(b))
6703 def _ppf(self, q, b):
6704 return np.sqrt(sc.chndtrix(q, 2, np.square(b)))
6706 def _pdf(self, x, b):
6707 # rice.pdf(x, b) = x * exp(-(x**2+b**2)/2) * I[0](x*b)
6708 #
6709 # We use (x**2 + b**2)/2 = ((x-b)**2)/2 + xb.
6710 # The factor of np.exp(-xb) is then included in the i0e function
6711 # in place of the modified Bessel function, i0, improving
6712 # numerical stability for large values of xb.
6713 return x * np.exp(-(x-b)*(x-b)/2.0) * sc.i0e(x*b)
6715 def _munp(self, n, b):
6716 nd2 = n/2.0
6717 n1 = 1 + nd2
6718 b2 = b*b/2.0
6719 return (2.0**(nd2) * np.exp(-b2) * sc.gamma(n1) *
6720 sc.hyp1f1(n1, 1, b2))
6723rice = rice_gen(a=0.0, name="rice")
6726# FIXME: PPF does not work.
6727class recipinvgauss_gen(rv_continuous):
6728 r"""A reciprocal inverse Gaussian continuous random variable.
6730 %(before_notes)s
6732 Notes
6733 -----
6734 The probability density function for `recipinvgauss` is:
6736 .. math::
6738 f(x, \mu) = \frac{1}{\sqrt{2\pi x}}
6739 \exp\left(\frac{-(1-\mu x)^2}{2\mu^2x}\right)
6741 for :math:`x \ge 0`.
6743 `recipinvgauss` takes ``mu`` as a shape parameter for :math:`\mu`.
6745 %(after_notes)s
6747 %(example)s
6749 """
6751 def _pdf(self, x, mu):
6752 # recipinvgauss.pdf(x, mu) =
6753 # 1/sqrt(2*pi*x) * exp(-(1-mu*x)**2/(2*x*mu**2))
6754 return 1.0/np.sqrt(2*np.pi*x)*np.exp(-(1-mu*x)**2.0 / (2*x*mu**2.0))
6756 def _logpdf(self, x, mu):
6757 return -(1-mu*x)**2.0 / (2*x*mu**2.0) - 0.5*np.log(2*np.pi*x)
6759 def _cdf(self, x, mu):
6760 trm1 = 1.0/mu - x
6761 trm2 = 1.0/mu + x
6762 isqx = 1.0/np.sqrt(x)
6763 return 1.0-_norm_cdf(isqx*trm1)-np.exp(2.0/mu)*_norm_cdf(-isqx*trm2)
6765 def _rvs(self, mu, size=None, random_state=None):
6766 return 1.0/random_state.wald(mu, 1.0, size=size)
6769recipinvgauss = recipinvgauss_gen(a=0.0, name='recipinvgauss')
6772class semicircular_gen(rv_continuous):
6773 r"""A semicircular continuous random variable.
6775 %(before_notes)s
6777 Notes
6778 -----
6779 The probability density function for `semicircular` is:
6781 .. math::
6783 f(x) = \frac{2}{\pi} \sqrt{1-x^2}
6785 for :math:`-1 \le x \le 1`.
6787 The distribution is a special case of `rdist` with `c = 3`.
6789 %(after_notes)s
6791 See Also
6792 --------
6793 rdist
6795 References
6796 ----------
6797 .. [1] "Wigner semicircle distribution",
6798 https://en.wikipedia.org/wiki/Wigner_semicircle_distribution
6800 %(example)s
6802 """
6803 def _pdf(self, x):
6804 return 2.0/np.pi*np.sqrt(1-x*x)
6806 def _logpdf(self, x):
6807 return np.log(2/np.pi) + 0.5*np.log1p(-x*x)
6809 def _cdf(self, x):
6810 return 0.5+1.0/np.pi*(x*np.sqrt(1-x*x) + np.arcsin(x))
6812 def _ppf(self, q):
6813 return rdist._ppf(q, 3)
6815 def _rvs(self, size=None, random_state=None):
6816 # generate values uniformly distributed on the area under the pdf
6817 # (semi-circle) by randomly generating the radius and angle
6818 r = np.sqrt(random_state.uniform(size=size))
6819 a = np.cos(np.pi * random_state.uniform(size=size))
6820 return r * a
6822 def _stats(self):
6823 return 0, 0.25, 0, -1.0
6825 def _entropy(self):
6826 return 0.64472988584940017414
6829semicircular = semicircular_gen(a=-1.0, b=1.0, name="semicircular")
6832class skew_norm_gen(rv_continuous):
6833 r"""A skew-normal random variable.
6835 %(before_notes)s
6837 Notes
6838 -----
6839 The pdf is::
6841 skewnorm.pdf(x, a) = 2 * norm.pdf(x) * norm.cdf(a*x)
6843 `skewnorm` takes a real number :math:`a` as a skewness parameter
6844 When ``a = 0`` the distribution is identical to a normal distribution
6845 (`norm`). `rvs` implements the method of [1]_.
6847 %(after_notes)s
6849 %(example)s
6851 References
6852 ----------
6853 .. [1] A. Azzalini and A. Capitanio (1999). Statistical applications of the
6854 multivariate skew-normal distribution. J. Roy. Statist. Soc., B 61, 579-602.
6855 https://arxiv.org/abs/0911.2093
6857 """
6858 def _argcheck(self, a):
6859 return np.isfinite(a)
6861 def _pdf(self, x, a):
6862 return 2.*_norm_pdf(x)*_norm_cdf(a*x)
6864 def _cdf_single(self, x, *args):
6865 _a, _b = self._get_support(*args)
6866 if x <= 0:
6867 cdf = integrate.quad(self._pdf, _a, x, args=args)[0]
6868 else:
6869 t1 = integrate.quad(self._pdf, _a, 0, args=args)[0]
6870 t2 = integrate.quad(self._pdf, 0, x, args=args)[0]
6871 cdf = t1 + t2
6872 if cdf > 1:
6873 # Presumably numerical noise, e.g. 1.0000000000000002
6874 cdf = 1.0
6875 return cdf
6877 def _sf(self, x, a):
6878 return self._cdf(-x, -a)
6880 def _rvs(self, a, size=None, random_state=None):
6881 u0 = random_state.normal(size=size)
6882 v = random_state.normal(size=size)
6883 d = a/np.sqrt(1 + a**2)
6884 u1 = d*u0 + v*np.sqrt(1 - d**2)
6885 return np.where(u0 >= 0, u1, -u1)
6887 def _stats(self, a, moments='mvsk'):
6888 output = [None, None, None, None]
6889 const = np.sqrt(2/np.pi) * a/np.sqrt(1 + a**2)
6891 if 'm' in moments:
6892 output[0] = const
6893 if 'v' in moments:
6894 output[1] = 1 - const**2
6895 if 's' in moments:
6896 output[2] = ((4 - np.pi)/2) * (const/np.sqrt(1 - const**2))**3
6897 if 'k' in moments:
6898 output[3] = (2*(np.pi - 3)) * (const**4/(1 - const**2)**2)
6900 return output
6903skewnorm = skew_norm_gen(name='skewnorm')
6906class trapz_gen(rv_continuous):
6907 r"""A trapezoidal continuous random variable.
6909 %(before_notes)s
6911 Notes
6912 -----
6913 The trapezoidal distribution can be represented with an up-sloping line
6914 from ``loc`` to ``(loc + c*scale)``, then constant to ``(loc + d*scale)``
6915 and then downsloping from ``(loc + d*scale)`` to ``(loc+scale)``. This
6916 defines the trapezoid base from ``loc`` to ``(loc+scale)`` and the flat
6917 top from ``c`` to ``d`` proportional to the position along the base
6918 with ``0 <= c <= d <= 1``. When ``c=d``, this is equivalent to `triang`
6919 with the same values for `loc`, `scale` and `c`.
6920 The method of [1]_ is used for computing moments.
6922 `trapz` takes :math:`c` and :math:`d` as shape parameters.
6924 %(after_notes)s
6926 The standard form is in the range [0, 1] with c the mode.
6927 The location parameter shifts the start to `loc`.
6928 The scale parameter changes the width from 1 to `scale`.
6930 %(example)s
6932 References
6933 ----------
6934 .. [1] Kacker, R.N. and Lawrence, J.F. (2007). Trapezoidal and triangular
6935 distributions for Type B evaluation of standard uncertainty.
6936 Metrologia 44, 117–127. https://doi.org/10.1088/0026-1394/44/2/003
6939 """
6940 def _argcheck(self, c, d):
6941 return (c >= 0) & (c <= 1) & (d >= 0) & (d <= 1) & (d >= c)
6943 def _pdf(self, x, c, d):
6944 u = 2 / (d-c+1)
6946 return _lazyselect([x < c,
6947 (c <= x) & (x <= d),
6948 x > d],
6949 [lambda x, c, d, u: u * x / c,
6950 lambda x, c, d, u: u,
6951 lambda x, c, d, u: u * (1-x) / (1-d)],
6952 (x, c, d, u))
6954 def _cdf(self, x, c, d):
6955 return _lazyselect([x < c,
6956 (c <= x) & (x <= d),
6957 x > d],
6958 [lambda x, c, d: x**2 / c / (d-c+1),
6959 lambda x, c, d: (c + 2 * (x-c)) / (d-c+1),
6960 lambda x, c, d: 1-((1-x) ** 2
6961 / (d-c+1) / (1-d))],
6962 (x, c, d))
6964 def _ppf(self, q, c, d):
6965 qc, qd = self._cdf(c, c, d), self._cdf(d, c, d)
6966 condlist = [q < qc, q <= qd, q > qd]
6967 choicelist = [np.sqrt(q * c * (1 + d - c)),
6968 0.5 * q * (1 + d - c) + 0.5 * c,
6969 1 - np.sqrt((1 - q) * (d - c + 1) * (1 - d))]
6970 return np.select(condlist, choicelist)
6972 def _munp(self, n, c, d):
6973 # Using the parameterization from Kacker, 2007, with
6974 # a=bottom left, c=top left, d=top right, b=bottom right, then
6975 # E[X^n] = h/(n+1)/(n+2) [(b^{n+2}-d^{n+2})/(b-d)
6976 # - ((c^{n+2} - a^{n+2})/(c-a)]
6977 # with h = 2/((b-a) - (d-c)). The corresponding parameterization
6978 # in scipy, has a'=loc, c'=loc+c*scale, d'=loc+d*scale, b'=loc+scale,
6979 # which for standard form reduces to a'=0, b'=1, c'=c, d'=d.
6980 # Substituting into E[X^n] gives the bd' term as (1 - d^{n+2})/(1 - d)
6981 # and the ac' term as c^{n-1} for the standard form. The bd' term has
6982 # numerical difficulties near d=1, so replace (1 - d^{n+2})/(1-d)
6983 # with expm1((n+2)*log(d))/(d-1).
6984 # Testing with n=18 for c=(1e-30,1-eps) shows that this is stable.
6985 # We still require an explicit test for d=1 to prevent divide by zero,
6986 # and now a test for d=0 to prevent log(0).
6987 ab_term = c**(n+1)
6988 dc_term = _lazyselect(
6989 [d == 0.0, (0.0 < d) & (d < 1.0), d == 1.0],
6990 [lambda d: 1.0,
6991 lambda d: np.expm1((n+2) * np.log(d)) / (d-1.0),
6992 lambda d: n+2],
6993 [d])
6994 val = 2.0 / (1.0+d-c) * (dc_term - ab_term) / ((n+1) * (n+2))
6995 return val
6997 def _entropy(self, c, d):
6998 # Using the parameterization from Wikipedia (van Dorp, 2003)
6999 # with a=bottom left, c=top left, d=top right, b=bottom right
7000 # gives a'=loc, b'=loc+c*scale, c'=loc+d*scale, d'=loc+scale,
7001 # which for loc=0, scale=1 is a'=0, b'=c, c'=d, d'=1.
7002 # Substituting into the entropy formula from Wikipedia gives
7003 # the following result.
7004 return 0.5 * (1.0-d+c) / (1.0+d-c) + np.log(0.5 * (1.0+d-c))
7007trapz = trapz_gen(a=0.0, b=1.0, name="trapz")
7010class triang_gen(rv_continuous):
7011 r"""A triangular continuous random variable.
7013 %(before_notes)s
7015 Notes
7016 -----
7017 The triangular distribution can be represented with an up-sloping line from
7018 ``loc`` to ``(loc + c*scale)`` and then downsloping for ``(loc + c*scale)``
7019 to ``(loc + scale)``.
7021 `triang` takes ``c`` as a shape parameter for :math:`c`.
7023 %(after_notes)s
7025 The standard form is in the range [0, 1] with c the mode.
7026 The location parameter shifts the start to `loc`.
7027 The scale parameter changes the width from 1 to `scale`.
7029 %(example)s
7031 """
7032 def _rvs(self, c, size=None, random_state=None):
7033 return random_state.triangular(0, c, 1, size)
7035 def _argcheck(self, c):
7036 return (c >= 0) & (c <= 1)
7038 def _pdf(self, x, c):
7039 # 0: edge case where c=0
7040 # 1: generalised case for x < c, don't use x <= c, as it doesn't cope
7041 # with c = 0.
7042 # 2: generalised case for x >= c, but doesn't cope with c = 1
7043 # 3: edge case where c=1
7044 r = _lazyselect([c == 0,
7045 x < c,
7046 (x >= c) & (c != 1),
7047 c == 1],
7048 [lambda x, c: 2 - 2 * x,
7049 lambda x, c: 2 * x / c,
7050 lambda x, c: 2 * (1 - x) / (1 - c),
7051 lambda x, c: 2 * x],
7052 (x, c))
7053 return r
7055 def _cdf(self, x, c):
7056 r = _lazyselect([c == 0,
7057 x < c,
7058 (x >= c) & (c != 1),
7059 c == 1],
7060 [lambda x, c: 2*x - x*x,
7061 lambda x, c: x * x / c,
7062 lambda x, c: (x*x - 2*x + c) / (c-1),
7063 lambda x, c: x * x],
7064 (x, c))
7065 return r
7067 def _ppf(self, q, c):
7068 return np.where(q < c, np.sqrt(c * q), 1-np.sqrt((1-c) * (1-q)))
7070 def _stats(self, c):
7071 return ((c+1.0)/3.0,
7072 (1.0-c+c*c)/18,
7073 np.sqrt(2)*(2*c-1)*(c+1)*(c-2) / (5*np.power((1.0-c+c*c), 1.5)),
7074 -3.0/5.0)
7076 def _entropy(self, c):
7077 return 0.5-np.log(2)
7080triang = triang_gen(a=0.0, b=1.0, name="triang")
7083class truncexpon_gen(rv_continuous):
7084 r"""A truncated exponential continuous random variable.
7086 %(before_notes)s
7088 Notes
7089 -----
7090 The probability density function for `truncexpon` is:
7092 .. math::
7094 f(x, b) = \frac{\exp(-x)}{1 - \exp(-b)}
7096 for :math:`0 <= x <= b`.
7098 `truncexpon` takes ``b`` as a shape parameter for :math:`b`.
7100 %(after_notes)s
7102 %(example)s
7104 """
7105 def _argcheck(self, b):
7106 return b > 0
7108 def _get_support(self, b):
7109 return self.a, b
7111 def _pdf(self, x, b):
7112 # truncexpon.pdf(x, b) = exp(-x) / (1-exp(-b))
7113 return np.exp(-x)/(-sc.expm1(-b))
7115 def _logpdf(self, x, b):
7116 return -x - np.log(-sc.expm1(-b))
7118 def _cdf(self, x, b):
7119 return sc.expm1(-x)/sc.expm1(-b)
7121 def _ppf(self, q, b):
7122 return -sc.log1p(q*sc.expm1(-b))
7124 def _munp(self, n, b):
7125 # wrong answer with formula, same as in continuous.pdf
7126 # return sc.gamman+1)-sc.gammainc1+n, b)
7127 if n == 1:
7128 return (1-(b+1)*np.exp(-b))/(-sc.expm1(-b))
7129 elif n == 2:
7130 return 2*(1-0.5*(b*b+2*b+2)*np.exp(-b))/(-sc.expm1(-b))
7131 else:
7132 # return generic for higher moments
7133 # return rv_continuous._mom1_sc(self, n, b)
7134 return self._mom1_sc(n, b)
7136 def _entropy(self, b):
7137 eB = np.exp(b)
7138 return np.log(eB-1)+(1+eB*(b-1.0))/(1.0-eB)
7141truncexpon = truncexpon_gen(a=0.0, name='truncexpon')
7144TRUNCNORM_TAIL_X = 30
7145TRUNCNORM_MAX_BRENT_ITERS = 40
7147def _truncnorm_get_delta_scalar(a, b):
7148 if (a > TRUNCNORM_TAIL_X) or (b < -TRUNCNORM_TAIL_X):
7149 return 0
7150 if a > 0:
7151 delta = _norm_sf(a) - _norm_sf(b)
7152 else:
7153 delta = _norm_cdf(b) - _norm_cdf(a)
7154 delta = max(delta, 0)
7155 return delta
7157def _truncnorm_get_delta(a, b):
7158 if np.isscalar(a) and np.isscalar(b):
7159 return _truncnorm_get_delta_scalar(a, b)
7160 a, b = np.atleast_1d(a), np.atleast_1d(b)
7161 if a.size == 1 and b.size == 1:
7162 return _truncnorm_get_delta_scalar(a.item(), b.item())
7163 delta = np.zeros(np.shape(a))
7164 condinner = (a <= TRUNCNORM_TAIL_X) & (b >= -TRUNCNORM_TAIL_X)
7165 conda = (a > 0) & condinner
7166 condb = (a <= 0) & condinner
7167 if np.any(conda):
7168 np.place(delta, conda, _norm_sf(a[conda]) - _norm_sf(b[conda]))
7169 if np.any(condb):
7170 np.place(delta, condb, _norm_cdf(b[condb]) - _norm_cdf(a[condb]))
7171 delta[delta < 0] = 0
7172 return delta
7174def _truncnorm_get_logdelta_scalar(a, b):
7175 if (a <= TRUNCNORM_TAIL_X) and (b >= -TRUNCNORM_TAIL_X):
7176 if a > 0:
7177 delta = _norm_sf(a) - _norm_sf(b)
7178 else:
7179 delta = _norm_cdf(b) - _norm_cdf(a)
7180 delta = max(delta, 0)
7181 if delta > 0:
7182 return np.log(delta)
7184 if b < 0 or (np.abs(a) >= np.abs(b)):
7185 nla, nlb = _norm_logcdf(a), _norm_logcdf(b)
7186 logdelta = nlb + np.log1p(-np.exp(nla - nlb))
7187 else:
7188 sla, slb = _norm_logsf(a), _norm_logsf(b)
7189 logdelta = sla + np.log1p(-np.exp(slb - sla))
7190 return logdelta
7193def _truncnorm_logpdf_scalar(x, a, b):
7194 with np.errstate(invalid='ignore'):
7195 if np.isscalar(x):
7196 if x < a:
7197 return -np.inf
7198 if x > b:
7199 return -np.inf
7200 shp = np.shape(x)
7201 x = np.atleast_1d(x)
7202 out = np.full_like(x, np.nan, dtype=np.double)
7203 condlta, condgtb = (x < a), (x > b)
7204 if np.any(condlta):
7205 np.place(out, condlta, -np.inf)
7206 if np.any(condgtb):
7207 np.place(out, condgtb, -np.inf)
7208 cond_inner = ~condlta & ~condgtb
7209 if np.any(cond_inner):
7210 _logdelta = _truncnorm_get_logdelta_scalar(a, b)
7211 np.place(out, cond_inner, _norm_logpdf(x[cond_inner]) - _logdelta)
7212 return (out[0] if (shp == ()) else out)
7215def _truncnorm_pdf_scalar(x, a, b):
7216 with np.errstate(invalid='ignore'):
7217 if np.isscalar(x):
7218 if x < a:
7219 return 0.0
7220 if x > b:
7221 return 0.0
7222 shp = np.shape(x)
7223 x = np.atleast_1d(x)
7224 out = np.full_like(x, np.nan, dtype=np.double)
7225 condlta, condgtb = (x < a), (x > b)
7226 if np.any(condlta):
7227 np.place(out, condlta, 0.0)
7228 if np.any(condgtb):
7229 np.place(out, condgtb, 0.0)
7230 cond_inner = ~condlta & ~condgtb
7231 if np.any(cond_inner):
7232 delta = _truncnorm_get_delta_scalar(a, b)
7233 if delta > 0:
7234 np.place(out, cond_inner, _norm_pdf(x[cond_inner]) / delta)
7235 else:
7236 np.place(out, cond_inner,
7237 np.exp(_truncnorm_logpdf_scalar(x[cond_inner], a, b)))
7238 return (out[0] if (shp == ()) else out)
7241def _truncnorm_logcdf_scalar(x, a, b):
7242 with np.errstate(invalid='ignore'):
7243 if np.isscalar(x):
7244 if x <= a:
7245 return -np.inf
7246 if x >= b:
7247 return 0
7248 shp = np.shape(x)
7249 x = np.atleast_1d(x)
7250 out = np.full_like(x, np.nan, dtype=np.double)
7251 condlea, condgeb = (x <= a), (x >= b)
7252 if np.any(condlea):
7253 np.place(out, condlea, -np.inf)
7254 if np.any(condgeb):
7255 np.place(out, condgeb, 0.0)
7256 cond_inner = ~condlea & ~condgeb
7257 if np.any(cond_inner):
7258 delta = _truncnorm_get_delta_scalar(a, b)
7259 if delta > 0:
7260 np.place(out, cond_inner,
7261 np.log((_norm_cdf(x[cond_inner]) - _norm_cdf(a)) / delta))
7262 else:
7263 with np.errstate(divide='ignore'):
7264 if a < 0:
7265 nla, nlb = _norm_logcdf(a), _norm_logcdf(b)
7266 tab = np.log1p(-np.exp(nla - nlb))
7267 nlx = _norm_logcdf(x[cond_inner])
7268 tax = np.log1p(-np.exp(nla - nlx))
7269 np.place(out, cond_inner, nlx + tax - (nlb + tab))
7270 else:
7271 sla = _norm_logsf(a)
7272 slb = _norm_logsf(b)
7273 np.place(out, cond_inner,
7274 np.log1p(-np.exp(_norm_logsf(x[cond_inner]) - sla))
7275 - np.log1p(-np.exp(slb - sla)))
7276 return (out[0] if (shp == ()) else out)
7279def _truncnorm_cdf_scalar(x, a, b):
7280 with np.errstate(invalid='ignore'):
7281 if np.isscalar(x):
7282 if x <= a:
7283 return -0
7284 if x >= b:
7285 return 1
7286 shp = np.shape(x)
7287 x = np.atleast_1d(x)
7288 out = np.full_like(x, np.nan, dtype=np.double)
7289 condlea, condgeb = (x <= a), (x >= b)
7290 if np.any(condlea):
7291 np.place(out, condlea, 0)
7292 if np.any(condgeb):
7293 np.place(out, condgeb, 1.0)
7294 cond_inner = ~condlea & ~condgeb
7295 if np.any(cond_inner):
7296 delta = _truncnorm_get_delta_scalar(a, b)
7297 if delta > 0:
7298 np.place(out, cond_inner,
7299 (_norm_cdf(x[cond_inner]) - _norm_cdf(a)) / delta)
7300 else:
7301 with np.errstate(divide='ignore'):
7302 np.place(out, cond_inner,
7303 np.exp(_truncnorm_logcdf_scalar(x[cond_inner], a, b)))
7304 return (out[0] if (shp == ()) else out)
7307def _truncnorm_logsf_scalar(x, a, b):
7308 with np.errstate(invalid='ignore'):
7309 if np.isscalar(x):
7310 if x <= a:
7311 return 0.0
7312 if x >= b:
7313 return -np.inf
7314 shp = np.shape(x)
7315 x = np.atleast_1d(x)
7316 out = np.full_like(x, np.nan, dtype=np.double)
7318 condlea, condgeb = (x <= a), (x >= b)
7319 if np.any(condlea):
7320 np.place(out, condlea, 0)
7321 if np.any(condgeb):
7322 np.place(out, condgeb, -np.inf)
7323 cond_inner = ~condlea & ~condgeb
7324 if np.any(cond_inner):
7325 delta = _truncnorm_get_delta_scalar(a, b)
7326 if delta > 0:
7327 np.place(out, cond_inner, np.log((_norm_sf(x[cond_inner]) - _norm_sf(b)) / delta))
7328 else:
7329 with np.errstate(divide='ignore'):
7330 if b < 0:
7331 nla, nlb = _norm_logcdf(a), _norm_logcdf(b)
7332 np.place(out, cond_inner,
7333 np.log1p(-np.exp(_norm_logcdf(x[cond_inner]) - nlb))
7334 - np.log1p(-np.exp(nla - nlb)))
7335 else:
7336 sla, slb = _norm_logsf(a), _norm_logsf(b)
7337 tab = np.log1p(-np.exp(slb - sla))
7338 slx = _norm_logsf(x[cond_inner])
7339 tax = np.log1p(-np.exp(slb - slx))
7340 np.place(out, cond_inner, slx + tax - (sla + tab))
7341 return (out[0] if (shp == ()) else out)
7344def _truncnorm_sf_scalar(x, a, b):
7345 with np.errstate(invalid='ignore'):
7346 if np.isscalar(x):
7347 if x <= a:
7348 return 1.0
7349 if x >= b:
7350 return 0.0
7351 shp = np.shape(x)
7352 x = np.atleast_1d(x)
7353 out = np.full_like(x, np.nan, dtype=np.double)
7355 condlea, condgeb = (x <= a), (x >= b)
7356 if np.any(condlea):
7357 np.place(out, condlea, 1.0)
7358 if np.any(condgeb):
7359 np.place(out, condgeb, 0.0)
7360 cond_inner = ~condlea & ~condgeb
7361 if np.any(cond_inner):
7362 delta = _truncnorm_get_delta_scalar(a, b)
7363 if delta > 0:
7364 np.place(out, cond_inner, (_norm_sf(x[cond_inner]) - _norm_sf(b)) / delta)
7365 else:
7366 np.place(out, cond_inner, np.exp(_truncnorm_logsf_scalar(x[cond_inner], a, b)))
7367 return (out[0] if (shp == ()) else out)
7370def _norm_logcdfprime(z):
7371 # derivative of special.log_ndtr (See special/cephes/ndtr.c)
7372 # Differentiate formula for log Phi(z)_truncnorm_ppf
7373 # log Phi(z) = -z^2/2 - log(-z) - log(2pi)/2 + log(1 + sum (-1)^n (2n-1)!! / z^(2n))
7374 # Convergence of series is slow for |z| < 10, but can use d(log Phi(z))/dz = dPhi(z)/dz / Phi(z)
7375 # Just take the first 10 terms because that is sufficient for use in _norm_ilogcdf
7376 assert np.all(z <= -10)
7377 lhs = -z - 1/z
7378 denom_cons = 1/z**2
7379 numerator = 1
7380 pwr = 1.0
7381 denom_total, numerator_total = 0, 0
7382 sign = -1
7383 for i in range(1, 11):
7384 pwr *= denom_cons
7385 numerator *= 2 * i - 1
7386 term = sign * numerator * pwr
7387 denom_total += term
7388 numerator_total += term * (2 * i) / z
7389 sign = -sign
7390 return lhs - numerator_total / (1 + denom_total)
7392def _norm_ilogcdf(y):
7393 """Inverse function to _norm_logcdf==sc.log_ndtr."""
7394 # Apply approximate Newton-Raphson
7395 # Only use for very negative values of y.
7396 # At minimum requires y <= -(log(2pi)+2^2)/2 ~= -2.9
7397 # Much better convergence for y <= -10
7398 z = -np.sqrt(-2 * (y + np.log(2*np.pi)/2))
7399 for _ in range(4):
7400 z = z - (_norm_logcdf(z) - y) / _norm_logcdfprime(z)
7401 return z
7404def _truncnorm_ppf_scalar(q, a, b):
7405 shp = np.shape(q)
7406 q = np.atleast_1d(q)
7407 out = np.zeros(np.shape(q))
7408 condle0, condge1 = (q <= 0), (q >= 1)
7409 if np.any(condle0):
7410 out[condle0] = a
7411 if np.any(condge1):
7412 out[condge1] = b
7413 delta = _truncnorm_get_delta_scalar(a, b)
7414 cond_inner = ~condle0 & ~condge1
7415 if np.any(cond_inner):
7416 qinner = q[cond_inner]
7417 if delta > 0:
7418 if a > 0:
7419 sa, sb = _norm_sf(a), _norm_sf(b)
7420 np.place(out, cond_inner,
7421 _norm_isf(qinner * sb + sa * (1.0 - qinner)))
7422 else:
7423 na, nb = _norm_cdf(a), _norm_cdf(b)
7424 np.place(out, cond_inner, _norm_ppf(qinner * nb + na * (1.0 - qinner)))
7425 elif np.isinf(b):
7426 np.place(out, cond_inner,
7427 -_norm_ilogcdf(np.log1p(-qinner) + _norm_logsf(a)))
7428 elif np.isinf(a):
7429 np.place(out, cond_inner,
7430 _norm_ilogcdf(np.log(q) + _norm_logcdf(b)))
7431 else:
7432 if b < 0:
7433 # Solve norm_logcdf(x) = norm_logcdf(a) + log1p(q * (expm1(norm_logcdf(b) - norm_logcdf(a)))
7434 # = nla + log1p(q * expm1(nlb - nla))
7435 # = nlb + log(q) + log1p((1-q) * exp(nla - nlb)/q)
7436 def _f_cdf(x, c):
7437 return _norm_logcdf(x) - c
7439 nla, nlb = _norm_logcdf(a), _norm_logcdf(b)
7440 values = nlb + np.log(q[cond_inner])
7441 C = np.exp(nla - nlb)
7442 if C:
7443 one_minus_q = (1 - q)[cond_inner]
7444 values += np.log1p(one_minus_q * C / q[cond_inner])
7445 x = [optimize.zeros.brentq(_f_cdf, a, b, args=(c,),
7446 maxiter=TRUNCNORM_MAX_BRENT_ITERS)for c in values]
7447 np.place(out, cond_inner, x)
7448 else:
7449 # Solve norm_logsf(x) = norm_logsf(b) + log1p((1-q) * (expm1(norm_logsf(a) - norm_logsf(b)))
7450 # = slb + log1p((1-q)[cond_inner] * expm1(sla - slb))
7451 # = sla + log(1-q) + log1p(q * np.exp(slb - sla)/(1-q))
7452 def _f_sf(x, c):
7453 return _norm_logsf(x) - c
7455 sla, slb = _norm_logsf(a), _norm_logsf(b)
7456 one_minus_q = (1-q)[cond_inner]
7457 values = sla + np.log(one_minus_q)
7458 C = np.exp(slb - sla)
7459 if C:
7460 values += np.log1p(q[cond_inner] * C / one_minus_q)
7461 x = [optimize.zeros.brentq(_f_sf, a, b, args=(c,),
7462 maxiter=TRUNCNORM_MAX_BRENT_ITERS) for c in values]
7463 np.place(out, cond_inner, x)
7464 out[out < a] = a
7465 out[out > b] = b
7466 return (out[0] if (shp == ()) else out)
7469class truncnorm_gen(rv_continuous):
7470 r"""A truncated normal continuous random variable.
7472 %(before_notes)s
7474 Notes
7475 -----
7476 The standard form of this distribution is a standard normal truncated to
7477 the range [a, b] --- notice that a and b are defined over the domain of the
7478 standard normal. To convert clip values for a specific mean and standard
7479 deviation, use::
7481 a, b = (myclip_a - my_mean) / my_std, (myclip_b - my_mean) / my_std
7483 `truncnorm` takes :math:`a` and :math:`b` as shape parameters.
7485 %(after_notes)s
7487 %(example)s
7489 """
7490 def _argcheck(self, a, b):
7491 return a < b
7493 def _get_support(self, a, b):
7494 return a, b
7496 def _pdf(self, x, a, b):
7497 if np.isscalar(a) and np.isscalar(b):
7498 return _truncnorm_pdf_scalar(x, a, b)
7499 a, b = np.atleast_1d(a), np.atleast_1d(b)
7500 if a.size == 1 and b.size == 1:
7501 return _truncnorm_pdf_scalar(x, a.item(), b.item())
7502 it = np.nditer([x, a, b, None], [],
7503 [['readonly'], ['readonly'], ['readonly'], ['writeonly','allocate']])
7504 for (_x, _a, _b, _ld) in it:
7505 _ld[...] = _truncnorm_pdf_scalar(_x, _a, _b)
7506 return it.operands[3]
7508 def _logpdf(self, x, a, b):
7509 if np.isscalar(a) and np.isscalar(b):
7510 return _truncnorm_logpdf_scalar(x, a, b)
7511 a, b = np.atleast_1d(a), np.atleast_1d(b)
7512 if a.size == 1 and b.size == 1:
7513 return _truncnorm_logpdf_scalar(x, a.item(), b.item())
7514 it = np.nditer([x, a, b, None], [],
7515 [['readonly'], ['readonly'], ['readonly'], ['writeonly','allocate']])
7516 for (_x, _a, _b, _ld) in it:
7517 _ld[...] = _truncnorm_logpdf_scalar(_x, _a, _b)
7518 return it.operands[3]
7520 def _cdf(self, x, a, b):
7521 if np.isscalar(a) and np.isscalar(b):
7522 return _truncnorm_cdf_scalar(x, a, b)
7523 a, b = np.atleast_1d(a), np.atleast_1d(b)
7524 if a.size == 1 and b.size == 1:
7525 return _truncnorm_cdf_scalar(x, a.item(), b.item())
7526 out = None
7527 it = np.nditer([x, a, b, out], [],
7528 [['readonly'], ['readonly'], ['readonly'], ['writeonly', 'allocate']])
7529 for (_x, _a, _b, _p) in it:
7530 _p[...] = _truncnorm_cdf_scalar(_x, _a, _b)
7531 return it.operands[3]
7533 def _logcdf(self, x, a, b):
7534 if np.isscalar(a) and np.isscalar(b):
7535 return _truncnorm_logcdf_scalar(x, a, b)
7536 a, b = np.atleast_1d(a), np.atleast_1d(b)
7537 if a.size == 1 and b.size == 1:
7538 return _truncnorm_logcdf_scalar(x, a.item(), b.item())
7539 it = np.nditer([x, a, b, None], [],
7540 [['readonly'], ['readonly'], ['readonly'], ['writeonly', 'allocate']])
7541 for (_x, _a, _b, _p) in it:
7542 _p[...] = _truncnorm_logcdf_scalar(_x, _a, _b)
7543 return it.operands[3]
7545 def _sf(self, x, a, b):
7546 if np.isscalar(a) and np.isscalar(b):
7547 return _truncnorm_sf_scalar(x, a, b)
7548 a, b = np.atleast_1d(a), np.atleast_1d(b)
7549 if a.size == 1 and b.size == 1:
7550 return _truncnorm_sf_scalar(x, a.item(), b.item())
7551 out = None
7552 it = np.nditer([x, a, b, out], [],
7553 [['readonly'], ['readonly'], ['readonly'], ['writeonly', 'allocate']])
7554 for (_x, _a, _b, _p) in it:
7555 _p[...] = _truncnorm_sf_scalar(_x, _a, _b)
7556 return it.operands[3]
7558 def _logsf(self, x, a, b):
7559 if np.isscalar(a) and np.isscalar(b):
7560 return _truncnorm_logsf_scalar(x, a, b)
7561 a, b = np.atleast_1d(a), np.atleast_1d(b)
7562 if a.size == 1 and b.size == 1:
7563 return _truncnorm_logsf_scalar(x, a.item(), b.item())
7564 out = None
7565 it = np.nditer([x, a, b, out], [],
7566 [['readonly'], ['readonly'], ['readonly'], ['writeonly', 'allocate']])
7567 for (_x, _a, _b, _p) in it:
7568 _p[...] = _truncnorm_logsf_scalar(_x, _a, _b)
7569 return it.operands[3]
7571 def _ppf(self, q, a, b):
7572 if np.isscalar(a) and np.isscalar(b):
7573 return _truncnorm_ppf_scalar(q, a, b)
7574 a, b = np.atleast_1d(a), np.atleast_1d(b)
7575 if a.size == 1 and b.size == 1:
7576 return _truncnorm_ppf_scalar(q, a.item(), b.item())
7578 out = None
7579 it = np.nditer([q, a, b, out], [],
7580 [['readonly'], ['readonly'], ['readonly'], ['writeonly', 'allocate']])
7581 for (_q, _a, _b, _x) in it:
7582 _x[...] = _truncnorm_ppf_scalar(_q, _a, _b)
7583 return it.operands[3]
7585 def _munp(self, n, a, b):
7586 def n_th_moment(n, a, b):
7587 """
7588 Returns n-th moment. Defined only if n >= 0.
7589 Function cannot broadcast due to the loop over n
7590 """
7591 pA, pB = self._pdf([a, b], a, b)
7592 probs = [pA, -pB]
7593 moments = [0, 1]
7594 for k in range(1, n+1):
7595 # a or b might be infinite, and the corresponding pdf value
7596 # is 0 in that case, but nan is returned for the
7597 # multiplication. However, as b->infinity, pdf(b)*b**k -> 0.
7598 # So it is safe to use _lazywhere to avoid the nan.
7599 vals = _lazywhere(probs, [probs, [a, b]],
7600 lambda x, y: x * y**(k-1), fillvalue=0)
7601 mk = np.sum(vals) + (k-1) * moments[-2]
7602 moments.append(mk)
7603 return moments[-1]
7605 return _lazywhere((n >= 0) & (a == a) & (b == b), (n, a, b),
7606 np.vectorize(n_th_moment, otypes=[np.float]), np.nan)
7608 def _stats(self, a, b, moments='mv'):
7609 pA, pB = self._pdf(np.array([a, b]), a, b)
7610 m1 = pA - pB
7611 mu = m1
7612 # use _lazywhere to avoid nan (See detailed comment in _munp)
7613 probs = [pA, -pB]
7614 vals = _lazywhere(probs, [probs, [a, b]], lambda x, y: x*y,
7615 fillvalue=0)
7616 m2 = 1 + np.sum(vals)
7617 vals = _lazywhere(probs, [probs, [a-mu, b-mu]], lambda x, y: x*y,
7618 fillvalue=0)
7619 # mu2 = m2 - mu**2, but not as numerically stable as:
7620 # mu2 = (a-mu)*pA - (b-mu)*pB + 1
7621 mu2 = 1 + np.sum(vals)
7622 vals = _lazywhere(probs, [probs, [a, b]], lambda x, y: x*y**2,
7623 fillvalue=0)
7624 m3 = 2*m1 + np.sum(vals)
7625 vals = _lazywhere(probs, [probs, [a, b]], lambda x, y: x*y**3,
7626 fillvalue=0)
7627 m4 = 3*m2 + np.sum(vals)
7629 mu3 = m3 + m1 * (-3*m2 + 2*m1**2)
7630 g1 = mu3 / np.power(mu2, 1.5)
7631 mu4 = m4 + m1*(-4*m3 + 3*m1*(2*m2 - m1**2))
7632 g2 = mu4 / mu2**2 - 3
7633 return mu, mu2, g1, g2
7635 def _rvs(self, a, b, size=None, random_state=None):
7636 # if a and b are scalar, use _rvs_scalar, otherwise need to create
7637 # output by iterating over parameters
7638 if np.isscalar(a) and np.isscalar(b):
7639 out = self._rvs_scalar(a, b, size, random_state=random_state)
7640 elif a.size == 1 and b.size == 1:
7641 out = self._rvs_scalar(a.item(), b.item(), size, random_state=random_state)
7642 else:
7643 # When this method is called, size will be a (possibly empty)
7644 # tuple of integers. It will not be None; if `size=None` is passed
7645 # to `rvs()`, size will be the empty tuple ().
7647 a, b = np.broadcast_arrays(a, b)
7648 # a and b now have the same shape.
7650 # `shp` is the shape of the blocks of random variates that are
7651 # generated for each combination of parameters associated with
7652 # broadcasting a and b.
7653 # bc is a tuple the same length as size. The values
7654 # in bc are bools. If bc[j] is True, it means that
7655 # entire axis is filled in for a given combination of the
7656 # broadcast arguments.
7657 shp, bc = _check_shape(a.shape, size)
7659 # `numsamples` is the total number of variates to be generated
7660 # for each combination of the input arguments.
7661 numsamples = int(np.prod(shp))
7663 # `out` is the array to be returned. It is filled in in the
7664 # loop below.
7665 out = np.empty(size)
7667 it = np.nditer([a, b],
7668 flags=['multi_index'],
7669 op_flags=[['readonly'], ['readonly']])
7670 while not it.finished:
7671 # Convert the iterator's multi_index into an index into the
7672 # `out` array where the call to _rvs_scalar() will be stored.
7673 # Where bc is True, we use a full slice; otherwise we use the
7674 # index value from it.multi_index. len(it.multi_index) might
7675 # be less than len(bc), and in that case we want to align these
7676 # two sequences to the right, so the loop variable j runs from
7677 # -len(size) to 0. This doesn't cause an IndexError, as
7678 # bc[j] will be True in those cases where it.multi_index[j]
7679 # would cause an IndexError.
7680 idx = tuple((it.multi_index[j] if not bc[j] else slice(None))
7681 for j in range(-len(size), 0))
7682 out[idx] = self._rvs_scalar(it[0], it[1], numsamples, random_state).reshape(shp)
7683 it.iternext()
7685 if size == ():
7686 out = out[()]
7687 return out
7689 def _rvs_scalar(self, a, b, numsamples=None, random_state=None):
7690 if not numsamples:
7691 numsamples = 1
7693 # prepare sampling of rvs
7694 size1d = tuple(np.atleast_1d(numsamples))
7695 N = np.prod(size1d) # number of rvs needed, reshape upon return
7696 # Calculate some rvs
7697 U = random_state.random_sample(N)
7698 x = self._ppf(U, a, b)
7699 rvs = np.reshape(x, size1d)
7700 return rvs
7703truncnorm = truncnorm_gen(name='truncnorm', momtype=1)
7706# FIXME: RVS does not work.
7707class tukeylambda_gen(rv_continuous):
7708 r"""A Tukey-Lamdba continuous random variable.
7710 %(before_notes)s
7712 Notes
7713 -----
7714 A flexible distribution, able to represent and interpolate between the
7715 following distributions:
7717 - Cauchy (:math:`lambda = -1`)
7718 - logistic (:math:`lambda = 0`)
7719 - approx Normal (:math:`lambda = 0.14`)
7720 - uniform from -1 to 1 (:math:`lambda = 1`)
7722 `tukeylambda` takes a real number :math:`lambda` (denoted ``lam``
7723 in the implementation) as a shape parameter.
7725 %(after_notes)s
7727 %(example)s
7729 """
7730 def _argcheck(self, lam):
7731 return np.ones(np.shape(lam), dtype=bool)
7733 def _pdf(self, x, lam):
7734 Fx = np.asarray(sc.tklmbda(x, lam))
7735 Px = Fx**(lam-1.0) + (np.asarray(1-Fx))**(lam-1.0)
7736 Px = 1.0/np.asarray(Px)
7737 return np.where((lam <= 0) | (abs(x) < 1.0/np.asarray(lam)), Px, 0.0)
7739 def _cdf(self, x, lam):
7740 return sc.tklmbda(x, lam)
7742 def _ppf(self, q, lam):
7743 return sc.boxcox(q, lam) - sc.boxcox1p(-q, lam)
7745 def _stats(self, lam):
7746 return 0, _tlvar(lam), 0, _tlkurt(lam)
7748 def _entropy(self, lam):
7749 def integ(p):
7750 return np.log(pow(p, lam-1)+pow(1-p, lam-1))
7751 return integrate.quad(integ, 0, 1)[0]
7754tukeylambda = tukeylambda_gen(name='tukeylambda')
7757class FitUniformFixedScaleDataError(FitDataError):
7758 def __init__(self, ptp, fscale):
7759 self.args = (
7760 "Invalid values in `data`. Maximum likelihood estimation with "
7761 "the uniform distribution and fixed scale requires that "
7762 "data.ptp() <= fscale, but data.ptp() = %r and fscale = %r." %
7763 (ptp, fscale),
7764 )
7767class uniform_gen(rv_continuous):
7768 r"""A uniform continuous random variable.
7770 In the standard form, the distribution is uniform on ``[0, 1]``. Using
7771 the parameters ``loc`` and ``scale``, one obtains the uniform distribution
7772 on ``[loc, loc + scale]``.
7774 %(before_notes)s
7776 %(example)s
7778 """
7779 def _rvs(self, size=None, random_state=None):
7780 return random_state.uniform(0.0, 1.0, size)
7782 def _pdf(self, x):
7783 return 1.0*(x == x)
7785 def _cdf(self, x):
7786 return x
7788 def _ppf(self, q):
7789 return q
7791 def _stats(self):
7792 return 0.5, 1.0/12, 0, -1.2
7794 def _entropy(self):
7795 return 0.0
7797 def fit(self, data, *args, **kwds):
7798 """
7799 Maximum likelihood estimate for the location and scale parameters.
7801 `uniform.fit` uses only the following parameters. Because exact
7802 formulas are used, the parameters related to optimization that are
7803 available in the `fit` method of other distributions are ignored
7804 here. The only positional argument accepted is `data`.
7806 Parameters
7807 ----------
7808 data : array_like
7809 Data to use in calculating the maximum likelihood estimate.
7810 floc : float, optional
7811 Hold the location parameter fixed to the specified value.
7812 fscale : float, optional
7813 Hold the scale parameter fixed to the specified value.
7815 Returns
7816 -------
7817 loc, scale : float
7818 Maximum likelihood estimates for the location and scale.
7820 Notes
7821 -----
7822 An error is raised if `floc` is given and any values in `data` are
7823 less than `floc`, or if `fscale` is given and `fscale` is less
7824 than ``data.max() - data.min()``. An error is also raised if both
7825 `floc` and `fscale` are given.
7827 Examples
7828 --------
7829 >>> from scipy.stats import uniform
7831 We'll fit the uniform distribution to `x`:
7833 >>> x = np.array([2, 2.5, 3.1, 9.5, 13.0])
7835 For a uniform distribution MLE, the location is the minimum of the
7836 data, and the scale is the maximum minus the minimum.
7838 >>> loc, scale = uniform.fit(x)
7839 >>> loc
7840 2.0
7841 >>> scale
7842 11.0
7844 If we know the data comes from a uniform distribution where the support
7845 starts at 0, we can use `floc=0`:
7847 >>> loc, scale = uniform.fit(x, floc=0)
7848 >>> loc
7849 0.0
7850 >>> scale
7851 13.0
7853 Alternatively, if we know the length of the support is 12, we can use
7854 `fscale=12`:
7856 >>> loc, scale = uniform.fit(x, fscale=12)
7857 >>> loc
7858 1.5
7859 >>> scale
7860 12.0
7862 In that last example, the support interval is [1.5, 13.5]. This
7863 solution is not unique. For example, the distribution with ``loc=2``
7864 and ``scale=12`` has the same likelihood as the one above. When
7865 `fscale` is given and it is larger than ``data.max() - data.min()``,
7866 the parameters returned by the `fit` method center the support over
7867 the interval ``[data.min(), data.max()]``.
7869 """
7870 if len(args) > 0:
7871 raise TypeError("Too many arguments.")
7873 floc = kwds.pop('floc', None)
7874 fscale = kwds.pop('fscale', None)
7876 _remove_optimizer_parameters(kwds)
7878 if floc is not None and fscale is not None:
7879 # This check is for consistency with `rv_continuous.fit`.
7880 raise ValueError("All parameters fixed. There is nothing to "
7881 "optimize.")
7883 data = np.asarray(data)
7885 if not np.isfinite(data).all():
7886 raise RuntimeError("The data contains non-finite values.")
7888 # MLE for the uniform distribution
7889 # --------------------------------
7890 # The PDF is
7891 #
7892 # f(x, loc, scale) = {1/scale for loc <= x <= loc + scale
7893 # {0 otherwise}
7894 #
7895 # The likelihood function is
7896 # L(x, loc, scale) = (1/scale)**n
7897 # where n is len(x), assuming loc <= x <= loc + scale for all x.
7898 # The log-likelihood is
7899 # l(x, loc, scale) = -n*log(scale)
7900 # The log-likelihood is maximized by making scale as small as possible,
7901 # while keeping loc <= x <= loc + scale. So if neither loc nor scale
7902 # are fixed, the log-likelihood is maximized by choosing
7903 # loc = x.min()
7904 # scale = x.ptp()
7905 # If loc is fixed, it must be less than or equal to x.min(), and then
7906 # the scale is
7907 # scale = x.max() - loc
7908 # If scale is fixed, it must not be less than x.ptp(). If scale is
7909 # greater than x.ptp(), the solution is not unique. Note that the
7910 # likelihood does not depend on loc, except for the requirement that
7911 # loc <= x <= loc + scale. All choices of loc for which
7912 # x.max() - scale <= loc <= x.min()
7913 # have the same log-likelihood. In this case, we choose loc such that
7914 # the support is centered over the interval [data.min(), data.max()]:
7915 # loc = x.min() = 0.5*(scale - x.ptp())
7917 if fscale is None:
7918 # scale is not fixed.
7919 if floc is None:
7920 # loc is not fixed, scale is not fixed.
7921 loc = data.min()
7922 scale = data.ptp()
7923 else:
7924 # loc is fixed, scale is not fixed.
7925 loc = floc
7926 scale = data.max() - loc
7927 if data.min() < loc:
7928 raise FitDataError("uniform", lower=loc, upper=loc + scale)
7929 else:
7930 # loc is not fixed, scale is fixed.
7931 ptp = data.ptp()
7932 if ptp > fscale:
7933 raise FitUniformFixedScaleDataError(ptp=ptp, fscale=fscale)
7934 # If ptp < fscale, the ML estimate is not unique; see the comments
7935 # above. We choose the distribution for which the support is
7936 # centered over the interval [data.min(), data.max()].
7937 loc = data.min() - 0.5*(fscale - ptp)
7938 scale = fscale
7940 # We expect the return values to be floating point, so ensure it
7941 # by explicitly converting to float.
7942 return float(loc), float(scale)
7945uniform = uniform_gen(a=0.0, b=1.0, name='uniform')
7948class vonmises_gen(rv_continuous):
7949 r"""A Von Mises continuous random variable.
7951 %(before_notes)s
7953 Notes
7954 -----
7955 The probability density function for `vonmises` and `vonmises_line` is:
7957 .. math::
7959 f(x, \kappa) = \frac{ \exp(\kappa \cos(x)) }{ 2 \pi I_0(\kappa) }
7961 for :math:`-\pi \le x \le \pi`, :math:`\kappa > 0`. :math:`I_0` is the
7962 modified Bessel function of order zero (`scipy.special.i0`).
7964 `vonmises` is a circular distribution which does not restrict the
7965 distribution to a fixed interval. Currently, there is no circular
7966 distribution framework in scipy. The ``cdf`` is implemented such that
7967 ``cdf(x + 2*np.pi) == cdf(x) + 1``.
7969 `vonmises_line` is the same distribution, defined on :math:`[-\pi, \pi]`
7970 on the real line. This is a regular (i.e. non-circular) distribution.
7972 `vonmises` and `vonmises_line` take ``kappa`` as a shape parameter.
7974 %(after_notes)s
7976 %(example)s
7978 """
7979 def _rvs(self, kappa, size=None, random_state=None):
7980 return random_state.vonmises(0.0, kappa, size=size)
7982 def _pdf(self, x, kappa):
7983 # vonmises.pdf(x, \kappa) = exp(\kappa * cos(x)) / (2*pi*I[0](\kappa))
7984 return np.exp(kappa * np.cos(x)) / (2*np.pi*sc.i0(kappa))
7986 def _cdf(self, x, kappa):
7987 return _stats.von_mises_cdf(kappa, x)
7989 def _stats_skip(self, kappa):
7990 return 0, None, 0, None
7992 def _entropy(self, kappa):
7993 return (-kappa * sc.i1(kappa) / sc.i0(kappa) +
7994 np.log(2 * np.pi * sc.i0(kappa)))
7997vonmises = vonmises_gen(name='vonmises')
7998vonmises_line = vonmises_gen(a=-np.pi, b=np.pi, name='vonmises_line')
8001class wald_gen(invgauss_gen):
8002 r"""A Wald continuous random variable.
8004 %(before_notes)s
8006 Notes
8007 -----
8008 The probability density function for `wald` is:
8010 .. math::
8012 f(x) = \frac{1}{\sqrt{2\pi x^3}} \exp(- \frac{ (x-1)^2 }{ 2x })
8014 for :math:`x >= 0`.
8016 `wald` is a special case of `invgauss` with ``mu=1``.
8018 %(after_notes)s
8020 %(example)s
8021 """
8022 _support_mask = rv_continuous._open_support_mask
8024 def _rvs(self, size=None, random_state=None):
8025 return random_state.wald(1.0, 1.0, size=size)
8027 def _pdf(self, x):
8028 # wald.pdf(x) = 1/sqrt(2*pi*x**3) * exp(-(x-1)**2/(2*x))
8029 return invgauss._pdf(x, 1.0)
8031 def _logpdf(self, x):
8032 return invgauss._logpdf(x, 1.0)
8034 def _cdf(self, x):
8035 return invgauss._cdf(x, 1.0)
8037 def _stats(self):
8038 return 1.0, 1.0, 3.0, 15.0
8041wald = wald_gen(a=0.0, name="wald")
8044class wrapcauchy_gen(rv_continuous):
8045 r"""A wrapped Cauchy continuous random variable.
8047 %(before_notes)s
8049 Notes
8050 -----
8051 The probability density function for `wrapcauchy` is:
8053 .. math::
8055 f(x, c) = \frac{1-c^2}{2\pi (1+c^2 - 2c \cos(x))}
8057 for :math:`0 \le x \le 2\pi`, :math:`0 < c < 1`.
8059 `wrapcauchy` takes ``c`` as a shape parameter for :math:`c`.
8061 %(after_notes)s
8063 %(example)s
8065 """
8066 def _argcheck(self, c):
8067 return (c > 0) & (c < 1)
8069 def _pdf(self, x, c):
8070 # wrapcauchy.pdf(x, c) = (1-c**2) / (2*pi*(1+c**2-2*c*cos(x)))
8071 return (1.0-c*c)/(2*np.pi*(1+c*c-2*c*np.cos(x)))
8073 def _cdf(self, x, c):
8074 output = np.zeros(x.shape, dtype=x.dtype)
8075 val = (1.0+c)/(1.0-c)
8076 c1 = x < np.pi
8077 c2 = 1-c1
8078 xp = np.extract(c1, x)
8079 xn = np.extract(c2, x)
8080 if np.any(xn):
8081 valn = np.extract(c2, np.ones_like(x)*val)
8082 xn = 2*np.pi - xn
8083 yn = np.tan(xn/2.0)
8084 on = 1.0-1.0/np.pi*np.arctan(valn*yn)
8085 np.place(output, c2, on)
8086 if np.any(xp):
8087 valp = np.extract(c1, np.ones_like(x)*val)
8088 yp = np.tan(xp/2.0)
8089 op = 1.0/np.pi*np.arctan(valp*yp)
8090 np.place(output, c1, op)
8091 return output
8093 def _ppf(self, q, c):
8094 val = (1.0-c)/(1.0+c)
8095 rcq = 2*np.arctan(val*np.tan(np.pi*q))
8096 rcmq = 2*np.pi-2*np.arctan(val*np.tan(np.pi*(1-q)))
8097 return np.where(q < 1.0/2, rcq, rcmq)
8099 def _entropy(self, c):
8100 return np.log(2*np.pi*(1-c*c))
8103wrapcauchy = wrapcauchy_gen(a=0.0, b=2*np.pi, name='wrapcauchy')
8106class gennorm_gen(rv_continuous):
8107 r"""A generalized normal continuous random variable.
8109 %(before_notes)s
8111 Notes
8112 -----
8113 The probability density function for `gennorm` is [1]_:
8115 .. math::
8117 f(x, \beta) = \frac{\beta}{2 \Gamma(1/\beta)} \exp(-|x|^\beta)
8119 :math:`\Gamma` is the gamma function (`scipy.special.gamma`).
8121 `gennorm` takes ``beta`` as a shape parameter for :math:`\beta`.
8122 For :math:`\beta = 1`, it is identical to a Laplace distribution.
8123 For :math:`\beta = 2`, it is identical to a normal distribution
8124 (with ``scale=1/sqrt(2)``).
8126 See Also
8127 --------
8128 laplace : Laplace distribution
8129 norm : normal distribution
8131 References
8132 ----------
8134 .. [1] "Generalized normal distribution, Version 1",
8135 https://en.wikipedia.org/wiki/Generalized_normal_distribution#Version_1
8137 %(example)s
8139 """
8141 def _pdf(self, x, beta):
8142 return np.exp(self._logpdf(x, beta))
8144 def _logpdf(self, x, beta):
8145 return np.log(0.5*beta) - sc.gammaln(1.0/beta) - abs(x)**beta
8147 def _cdf(self, x, beta):
8148 c = 0.5 * np.sign(x)
8149 # evaluating (.5 + c) first prevents numerical cancellation
8150 return (0.5 + c) - c * sc.gammaincc(1.0/beta, abs(x)**beta)
8152 def _ppf(self, x, beta):
8153 c = np.sign(x - 0.5)
8154 # evaluating (1. + c) first prevents numerical cancellation
8155 return c * sc.gammainccinv(1.0/beta, (1.0 + c) - 2.0*c*x)**(1.0/beta)
8157 def _sf(self, x, beta):
8158 return self._cdf(-x, beta)
8160 def _isf(self, x, beta):
8161 return -self._ppf(x, beta)
8163 def _stats(self, beta):
8164 c1, c3, c5 = sc.gammaln([1.0/beta, 3.0/beta, 5.0/beta])
8165 return 0., np.exp(c3 - c1), 0., np.exp(c5 + c1 - 2.0*c3) - 3.
8167 def _entropy(self, beta):
8168 return 1. / beta - np.log(.5 * beta) + sc.gammaln(1. / beta)
8171gennorm = gennorm_gen(name='gennorm')
8174class halfgennorm_gen(rv_continuous):
8175 r"""The upper half of a generalized normal continuous random variable.
8177 %(before_notes)s
8179 Notes
8180 -----
8181 The probability density function for `halfgennorm` is:
8183 .. math::
8185 f(x, \beta) = \frac{\beta}{\Gamma(1/\beta)} \exp(-|x|^\beta)
8187 for :math:`x > 0`. :math:`\Gamma` is the gamma function
8188 (`scipy.special.gamma`).
8190 `gennorm` takes ``beta`` as a shape parameter for :math:`\beta`.
8191 For :math:`\beta = 1`, it is identical to an exponential distribution.
8192 For :math:`\beta = 2`, it is identical to a half normal distribution
8193 (with ``scale=1/sqrt(2)``).
8195 See Also
8196 --------
8197 gennorm : generalized normal distribution
8198 expon : exponential distribution
8199 halfnorm : half normal distribution
8201 References
8202 ----------
8204 .. [1] "Generalized normal distribution, Version 1",
8205 https://en.wikipedia.org/wiki/Generalized_normal_distribution#Version_1
8207 %(example)s
8209 """
8211 def _pdf(self, x, beta):
8212 # beta
8213 # halfgennorm.pdf(x, beta) = ------------- exp(-|x|**beta)
8214 # gamma(1/beta)
8215 return np.exp(self._logpdf(x, beta))
8217 def _logpdf(self, x, beta):
8218 return np.log(beta) - sc.gammaln(1.0/beta) - x**beta
8220 def _cdf(self, x, beta):
8221 return sc.gammainc(1.0/beta, x**beta)
8223 def _ppf(self, x, beta):
8224 return sc.gammaincinv(1.0/beta, x)**(1.0/beta)
8226 def _sf(self, x, beta):
8227 return sc.gammaincc(1.0/beta, x**beta)
8229 def _isf(self, x, beta):
8230 return sc.gammainccinv(1.0/beta, x)**(1.0/beta)
8232 def _entropy(self, beta):
8233 return 1.0/beta - np.log(beta) + sc.gammaln(1.0/beta)
8236halfgennorm = halfgennorm_gen(a=0, name='halfgennorm')
8239class crystalball_gen(rv_continuous):
8240 r"""
8241 Crystalball distribution
8243 %(before_notes)s
8245 Notes
8246 -----
8247 The probability density function for `crystalball` is:
8249 .. math::
8251 f(x, \beta, m) = \begin{cases}
8252 N \exp(-x^2 / 2), &\text{for } x > -\beta\\
8253 N A (B - x)^{-m} &\text{for } x \le -\beta
8254 \end{cases}
8256 where :math:`A = (m / |\beta|)^n \exp(-\beta^2 / 2)`,
8257 :math:`B = m/|\beta| - |\beta|` and :math:`N` is a normalisation constant.
8259 `crystalball` takes :math:`\beta > 0` and :math:`m > 1` as shape
8260 parameters. :math:`\beta` defines the point where the pdf changes
8261 from a power-law to a Gaussian distribution. :math:`m` is the power
8262 of the power-law tail.
8264 References
8265 ----------
8266 .. [1] "Crystal Ball Function",
8267 https://en.wikipedia.org/wiki/Crystal_Ball_function
8269 %(after_notes)s
8271 .. versionadded:: 0.19.0
8273 %(example)s
8274 """
8276 def _pdf(self, x, beta, m):
8277 """
8278 Return PDF of the crystalball function.
8280 --
8281 | exp(-x**2 / 2), for x > -beta
8282 crystalball.pdf(x, beta, m) = N * |
8283 | A * (B - x)**(-m), for x <= -beta
8284 --
8285 """
8286 N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) +
8287 _norm_pdf_C * _norm_cdf(beta))
8289 def rhs(x, beta, m):
8290 return np.exp(-x**2 / 2)
8292 def lhs(x, beta, m):
8293 return ((m/beta)**m * np.exp(-beta**2 / 2.0) *
8294 (m/beta - beta - x)**(-m))
8296 return N * _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs)
8298 def _logpdf(self, x, beta, m):
8299 """
8300 Return the log of the PDF of the crystalball function.
8301 """
8302 N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) +
8303 _norm_pdf_C * _norm_cdf(beta))
8305 def rhs(x, beta, m):
8306 return -x**2/2
8308 def lhs(x, beta, m):
8309 return m*np.log(m/beta) - beta**2/2 - m*np.log(m/beta - beta - x)
8311 return np.log(N) + _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs)
8313 def _cdf(self, x, beta, m):
8314 """
8315 Return CDF of the crystalball function
8316 """
8317 N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) +
8318 _norm_pdf_C * _norm_cdf(beta))
8320 def rhs(x, beta, m):
8321 return ((m/beta) * np.exp(-beta**2 / 2.0) / (m-1) +
8322 _norm_pdf_C * (_norm_cdf(x) - _norm_cdf(-beta)))
8324 def lhs(x, beta, m):
8325 return ((m/beta)**m * np.exp(-beta**2 / 2.0) *
8326 (m/beta - beta - x)**(-m+1) / (m-1))
8328 return N * _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs)
8330 def _ppf(self, p, beta, m):
8331 N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) +
8332 _norm_pdf_C * _norm_cdf(beta))
8333 pbeta = N * (m/beta) * np.exp(-beta**2/2) / (m - 1)
8335 def ppf_less(p, beta, m):
8336 eb2 = np.exp(-beta**2/2)
8337 C = (m/beta) * eb2 / (m-1)
8338 N = 1/(C + _norm_pdf_C * _norm_cdf(beta))
8339 return (m/beta - beta -
8340 ((m - 1)*(m/beta)**(-m)/eb2*p/N)**(1/(1-m)))
8342 def ppf_greater(p, beta, m):
8343 eb2 = np.exp(-beta**2/2)
8344 C = (m/beta) * eb2 / (m-1)
8345 N = 1/(C + _norm_pdf_C * _norm_cdf(beta))
8346 return _norm_ppf(_norm_cdf(-beta) + (1/_norm_pdf_C)*(p/N - C))
8348 return _lazywhere(p < pbeta, (p, beta, m), f=ppf_less, f2=ppf_greater)
8350 def _munp(self, n, beta, m):
8351 """
8352 Returns the n-th non-central moment of the crystalball function.
8353 """
8354 N = 1.0 / (m/beta / (m-1) * np.exp(-beta**2 / 2.0) +
8355 _norm_pdf_C * _norm_cdf(beta))
8357 def n_th_moment(n, beta, m):
8358 """
8359 Returns n-th moment. Defined only if n+1 < m
8360 Function cannot broadcast due to the loop over n
8361 """
8362 A = (m/beta)**m * np.exp(-beta**2 / 2.0)
8363 B = m/beta - beta
8364 rhs = (2**((n-1)/2.0) * sc.gamma((n+1)/2) *
8365 (1.0 + (-1)**n * sc.gammainc((n+1)/2, beta**2 / 2)))
8366 lhs = np.zeros(rhs.shape)
8367 for k in range(n + 1):
8368 lhs += (sc.binom(n, k) * B**(n-k) * (-1)**k / (m - k - 1) *
8369 (m/beta)**(-m + k + 1))
8370 return A * lhs + rhs
8372 return N * _lazywhere(n + 1 < m, (n, beta, m),
8373 np.vectorize(n_th_moment, otypes=[np.float]),
8374 np.inf)
8376 def _argcheck(self, beta, m):
8377 """
8378 Shape parameter bounds are m > 1 and beta > 0.
8379 """
8380 return (m > 1) & (beta > 0)
8383crystalball = crystalball_gen(name='crystalball', longname="A Crystalball Function")
8386def _argus_phi(chi):
8387 """
8388 Utility function for the argus distribution
8389 used in the CDF and norm of the Argus Funktion
8390 """
8391 return _norm_cdf(chi) - chi * _norm_pdf(chi) - 0.5
8394class argus_gen(rv_continuous):
8395 r"""
8396 Argus distribution
8398 %(before_notes)s
8400 Notes
8401 -----
8402 The probability density function for `argus` is:
8404 .. math::
8406 f(x, \chi) = \frac{\chi^3}{\sqrt{2\pi} \Psi(\chi)} x \sqrt{1-x^2}
8407 \exp(-\chi^2 (1 - x^2)/2)
8409 for :math:`0 < x < 1` and :math:`\chi > 0`, where
8411 .. math::
8413 \Psi(\chi) = \Phi(\chi) - \chi \phi(\chi) - 1/2
8415 with :math:`\Phi` and :math:`\phi` being the CDF and PDF of a standard
8416 normal distribution, respectively.
8418 `argus` takes :math:`\chi` as shape a parameter.
8420 References
8421 ----------
8423 .. [1] "ARGUS distribution",
8424 https://en.wikipedia.org/wiki/ARGUS_distribution
8426 %(after_notes)s
8428 .. versionadded:: 0.19.0
8430 %(example)s
8431 """
8432 def _pdf(self, x, chi):
8433 y = 1.0 - x**2
8434 A = chi**3 / (_norm_pdf_C * _argus_phi(chi))
8435 return A * x * np.sqrt(y) * np.exp(-chi**2 * y / 2)
8437 def _cdf(self, x, chi):
8438 return 1.0 - self._sf(x, chi)
8440 def _sf(self, x, chi):
8441 return _argus_phi(chi * np.sqrt(1 - x**2)) / _argus_phi(chi)
8443 def _rvs(self, chi, size=None, random_state=None):
8444 chi = np.asarray(chi)
8445 if chi.size == 1:
8446 out = self._rvs_scalar(chi, numsamples=size,
8447 random_state=random_state)
8448 else:
8449 shp, bc = _check_shape(chi.shape, size)
8450 numsamples = int(np.prod(shp))
8451 out = np.empty(size)
8452 it = np.nditer([chi],
8453 flags=['multi_index'],
8454 op_flags=[['readonly']])
8455 while not it.finished:
8456 idx = tuple((it.multi_index[j] if not bc[j] else slice(None))
8457 for j in range(-len(size), 0))
8458 r = self._rvs_scalar(it[0], numsamples=numsamples,
8459 random_state=random_state)
8460 out[idx] = r.reshape(shp)
8461 it.iternext()
8463 if size == ():
8464 out = out[()]
8465 return out
8467 def _rvs_scalar(self, chi, numsamples=None, random_state=None):
8468 # if chi <= 2.611:
8469 # use rejection method, see Devroye:
8470 # Non-Uniform Random Variate Generation, 1986, section II.3.2.
8471 # write: self.pdf = c * g(x) * h(x), where
8472 # h is [0,1]-valued and g is a density
8473 # g(x) = d1 * chi**2 * x * exp(-chi**2 * (1 - x**2) / 2), 0 <= x <= 1
8474 # h(x) = sqrt(1 - x**2), 0 <= x <= 1
8475 # Integrating g, we get:
8476 # G(x) = d1 * exp(-chi**2 * (1 - x**2) / 2) - d2
8477 # d1 and d2 are determined by G(0) = 0 and G(1) = 1
8478 # d1 = 1 / (1 - exp(-0.5 * chi**2))
8479 # d2 = 1 / (exp(0.5 * chi**2) - 1)
8480 # => G(x) = (exp(chi**2 * x**2 /2) - 1) / (exp(chi**2 / 2) - 1)
8481 # expected number of iterations is c with
8482 # c = -np.expm1(-0.5 * chi**2) * chi / (_norm_pdf_C * _argus_phi(chi))
8483 # note that G can be inverted easily, so we can sample
8484 # rvs from this distribution
8485 # G_inv(y) = sqrt(2 * log(1 + (exp(chi**2 / 2) - 1) * y) / chi**2)
8486 # to avoid an overflow of exp(chi**2 / 2), it is convenient to write
8487 # G_inv(y) = sqrt(1 + 2 * log(exp(-chi**2 / 2) * (1-y) + y) / chi**2)
8488 #
8489 # if chi > 2.611:
8490 # use ratio of uniforms method applied to a transformed variable of X
8491 # (X is ARGUS with parameter chi):
8492 # Y = chi * sqrt(1 - X**2) has density proportional to
8493 # u**2 * exp(-u**2 / 2) on [0, chi] (Maxwell distribution conditioned
8494 # on [0, chi]). Apply ratio of uniforms to this density to generate
8495 # samples of Y and convert back to X
8496 #
8497 # The expected number of iterations using the rejection method
8498 # increases with increasing chi, whereas the expected number of
8499 # iterations using the ratio of uniforms method decreases with
8500 # increasing chi. The crossover occurs where
8501 # chi*(1 - exp(-0.5*chi**2)) = 8*sqrt(2)*exp(-1.5) => chi ~ 2.611
8502 # Switching algorithms at chi=2.611 means that the expected number of
8503 # iterations is always below 2.2.
8505 if chi <= 2.611:
8506 # use rejection method
8507 size1d = tuple(np.atleast_1d(numsamples))
8508 N = int(np.prod(size1d))
8509 x = np.zeros(N)
8510 echi = np.exp(-chi**2 / 2)
8511 simulated = 0
8512 while simulated < N:
8513 k = N - simulated
8514 u = random_state.uniform(size=k)
8515 v = random_state.uniform(size=k)
8516 # acceptance condition: u <= h(G_inv(v)). This simplifies to
8517 z = 2 * np.log(echi * (1 - v) + v) / chi**2
8518 accept = (u**2 + z <= 0)
8519 num_accept = np.sum(accept)
8520 if num_accept > 0:
8521 # rvs follow a distribution with density g: rvs = G_inv(v)
8522 rvs = np.sqrt(1 + z[accept])
8523 x[simulated:(simulated + num_accept)] = rvs
8524 simulated += num_accept
8526 return np.reshape(x, size1d)
8527 else:
8528 # use ratio of uniforms method
8529 def f(x):
8530 return np.where((x >= 0) & (x <= chi),
8531 np.exp(2*np.log(x) - x**2/2), 0)
8533 umax = np.sqrt(2) / np.exp(0.5)
8534 vmax = 4 / np.exp(1)
8535 z = rvs_ratio_uniforms(f, umax, 0, vmax, size=numsamples,
8536 random_state=random_state)
8537 return np.sqrt(1 - z*z / chi**2)
8539 def _stats(self, chi):
8540 chi2 = chi**2
8541 phi = _argus_phi(chi)
8542 m = np.sqrt(np.pi/8) * chi * np.exp(-chi2/4) * sc.iv(1, chi2/4) / phi
8543 v = (1 - 3 / chi2 + chi * _norm_pdf(chi) / phi) - m**2
8544 return m, v, None, None
8547argus = argus_gen(name='argus', longname="An Argus Function", a=0.0, b=1.0)
8550class rv_histogram(rv_continuous):
8551 """
8552 Generates a distribution given by a histogram.
8553 This is useful to generate a template distribution from a binned
8554 datasample.
8556 As a subclass of the `rv_continuous` class, `rv_histogram` inherits from it
8557 a collection of generic methods (see `rv_continuous` for the full list),
8558 and implements them based on the properties of the provided binned
8559 datasample.
8561 Parameters
8562 ----------
8563 histogram : tuple of array_like
8564 Tuple containing two array_like objects
8565 The first containing the content of n bins
8566 The second containing the (n+1) bin boundaries
8567 In particular the return value np.histogram is accepted
8569 Notes
8570 -----
8571 There are no additional shape parameters except for the loc and scale.
8572 The pdf is defined as a stepwise function from the provided histogram
8573 The cdf is a linear interpolation of the pdf.
8575 .. versionadded:: 0.19.0
8577 Examples
8578 --------
8580 Create a scipy.stats distribution from a numpy histogram
8582 >>> import scipy.stats
8583 >>> import numpy as np
8584 >>> data = scipy.stats.norm.rvs(size=100000, loc=0, scale=1.5, random_state=123)
8585 >>> hist = np.histogram(data, bins=100)
8586 >>> hist_dist = scipy.stats.rv_histogram(hist)
8588 Behaves like an ordinary scipy rv_continuous distribution
8590 >>> hist_dist.pdf(1.0)
8591 0.20538577847618705
8592 >>> hist_dist.cdf(2.0)
8593 0.90818568543056499
8595 PDF is zero above (below) the highest (lowest) bin of the histogram,
8596 defined by the max (min) of the original dataset
8598 >>> hist_dist.pdf(np.max(data))
8599 0.0
8600 >>> hist_dist.cdf(np.max(data))
8601 1.0
8602 >>> hist_dist.pdf(np.min(data))
8603 7.7591907244498314e-05
8604 >>> hist_dist.cdf(np.min(data))
8605 0.0
8607 PDF and CDF follow the histogram
8609 >>> import matplotlib.pyplot as plt
8610 >>> X = np.linspace(-5.0, 5.0, 100)
8611 >>> plt.title("PDF from Template")
8612 >>> plt.hist(data, density=True, bins=100)
8613 >>> plt.plot(X, hist_dist.pdf(X), label='PDF')
8614 >>> plt.plot(X, hist_dist.cdf(X), label='CDF')
8615 >>> plt.show()
8617 """
8618 _support_mask = rv_continuous._support_mask
8620 def __init__(self, histogram, *args, **kwargs):
8621 """
8622 Create a new distribution using the given histogram
8624 Parameters
8625 ----------
8626 histogram : tuple of array_like
8627 Tuple containing two array_like objects
8628 The first containing the content of n bins
8629 The second containing the (n+1) bin boundaries
8630 In particular the return value np.histogram is accepted
8631 """
8632 self._histogram = histogram
8633 if len(histogram) != 2:
8634 raise ValueError("Expected length 2 for parameter histogram")
8635 self._hpdf = np.asarray(histogram[0])
8636 self._hbins = np.asarray(histogram[1])
8637 if len(self._hpdf) + 1 != len(self._hbins):
8638 raise ValueError("Number of elements in histogram content "
8639 "and histogram boundaries do not match, "
8640 "expected n and n+1.")
8641 self._hbin_widths = self._hbins[1:] - self._hbins[:-1]
8642 self._hpdf = self._hpdf / float(np.sum(self._hpdf * self._hbin_widths))
8643 self._hcdf = np.cumsum(self._hpdf * self._hbin_widths)
8644 self._hpdf = np.hstack([0.0, self._hpdf, 0.0])
8645 self._hcdf = np.hstack([0.0, self._hcdf])
8646 # Set support
8647 kwargs['a'] = self.a = self._hbins[0]
8648 kwargs['b'] = self.b = self._hbins[-1]
8649 super(rv_histogram, self).__init__(*args, **kwargs)
8651 def _pdf(self, x):
8652 """
8653 PDF of the histogram
8654 """
8655 return self._hpdf[np.searchsorted(self._hbins, x, side='right')]
8657 def _cdf(self, x):
8658 """
8659 CDF calculated from the histogram
8660 """
8661 return np.interp(x, self._hbins, self._hcdf)
8663 def _ppf(self, x):
8664 """
8665 Percentile function calculated from the histogram
8666 """
8667 return np.interp(x, self._hcdf, self._hbins)
8669 def _munp(self, n):
8670 """Compute the n-th non-central moment."""
8671 integrals = (self._hbins[1:]**(n+1) - self._hbins[:-1]**(n+1)) / (n+1)
8672 return np.sum(self._hpdf[1:-1] * integrals)
8674 def _entropy(self):
8675 """Compute entropy of distribution"""
8676 res = _lazywhere(self._hpdf[1:-1] > 0.0,
8677 (self._hpdf[1:-1],),
8678 np.log,
8679 0.0)
8680 return -np.sum(self._hpdf[1:-1] * res * self._hbin_widths)
8682 def _updated_ctor_param(self):
8683 """
8684 Set the histogram as additional constructor argument
8685 """
8686 dct = super(rv_histogram, self)._updated_ctor_param()
8687 dct['histogram'] = self._histogram
8688 return dct
8691# Collect names of classes and objects in this module.
8692pairs = list(globals().items())
8693_distn_names, _distn_gen_names = get_distribution_names(pairs, rv_continuous)
8695__all__ = _distn_names + _distn_gen_names + ['rv_histogram']