Hide keyboard shortcuts

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 

9 

10import numpy as np 

11 

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) 

32 

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 

39 

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. 

45 

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. 

48 

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) 

56 

57 

58## Kolmogorov-Smirnov one-sided and two-sided test statistics 

59class ksone_gen(rv_continuous): 

60 r"""Kolmogorov-Smirnov one-sided test statistic distribution. 

61 

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). 

65 

66 %(before_notes)s 

67 

68 Notes 

69 ----- 

70 :math:`D_n^+` and :math:`D_n^-` are given by 

71 

72 .. math:: 

73 

74 D_n^+ &= \text{sup}_x (F_n(x) - F(x)),\\ 

75 D_n^- &= \text{sup}_x (F(x) - F_n(x)),\\ 

76 

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`. 

81 

82 %(after_notes)s 

83 

84 See Also 

85 -------- 

86 kstwobign, kstwo, kstest 

87 

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). 

93 

94 %(example)s 

95 

96 """ 

97 def _pdf(self, x, n): 

98 return -scu._smirnovp(n, x) 

99 

100 def _cdf(self, x, n): 

101 return scu._smirnovc(n, x) 

102 

103 def _sf(self, x, n): 

104 return sc.smirnov(n, x) 

105 

106 def _ppf(self, q, n): 

107 return scu._smirnovci(n, q) 

108 

109 def _isf(self, q, n): 

110 return sc.smirnovi(n, q) 

111 

112 

113ksone = ksone_gen(a=0.0, b=1.0, name='ksone') 

114 

115 

116class kstwo_gen(rv_continuous): 

117 r"""Kolmogorov-Smirnov two-sided test statistic distribution. 

118 

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). 

122 

123 %(before_notes)s 

124 

125 Notes 

126 ----- 

127 :math:`D_n` is given by 

128 

129 .. math:: 

130 

131 D_n &= \text{sup}_x |F_n(x) - F(x)| 

132 

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`. 

137 

138 %(after_notes)s 

139 

140 See Also 

141 -------- 

142 kstwobign, ksone, kstest 

143 

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). 

149 

150 %(example)s 

151 

152 """ 

153 def _get_support(self, n): 

154 return (0.5/(n if not isinstance(n, Iterable) else np.asanyarray(n)), 

155 1.0) 

156 

157 def _pdf(self, x, n): 

158 return kolmognp(n, x) 

159 

160 def _cdf(self, x, n): 

161 return kolmogn(n, x) 

162 

163 def _sf(self, x, n): 

164 return kolmogn(n, x, cdf=False) 

165 

166 def _ppf(self, q, n): 

167 return kolmogni(n, q, cdf=True) 

168 

169 def _isf(self, q, n): 

170 return kolmogni(n, q, cdf=False) 

171 

172 

173# Use the pdf, (not the ppf) to compute moments 

174kstwo = kstwo_gen(momtype=0, a=0.0, b=1.0, name='kstwo') 

175 

176 

177class kstwobign_gen(rv_continuous): 

178 r"""Limiting distribution of scaled Kolmogorov-Smirnov two-sided test statistic. 

179 

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`). 

184 

185 %(before_notes)s 

186 

187 Notes 

188 ----- 

189 :math:`\sqrt{n} D_n` is given by 

190 

191 .. math:: 

192 

193 D_n = \text{sup}_x |F_n(x) - F(x)| 

194 

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`. 

199 

200 %(after_notes)s 

201 

202 See Also 

203 -------- 

204 ksone, kstwo, kstest 

205 

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). 

210 

211 %(example)s 

212 

213 """ 

214 def _pdf(self, x): 

215 return -scu._kolmogp(x) 

216 

217 def _cdf(self, x): 

218 return scu._kolmogc(x) 

219 

220 def _sf(self, x): 

221 return sc.kolmogorov(x) 

222 

223 def _ppf(self, q): 

224 return scu._kolmogci(q) 

225 

226 def _isf(self, q): 

227 return sc.kolmogi(q) 

228 

229 

230kstwobign = kstwobign_gen(a=0.0, name='kstwobign') 

231 

232 

233## Normal distribution 

234 

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) 

240 

241 

242def _norm_pdf(x): 

243 return np.exp(-x**2/2.0) / _norm_pdf_C 

244 

245 

246def _norm_logpdf(x): 

247 return -x**2 / 2.0 - _norm_pdf_logC 

248 

249 

250def _norm_cdf(x): 

251 return sc.ndtr(x) 

252 

253 

254def _norm_logcdf(x): 

255 return sc.log_ndtr(x) 

256 

257 

258def _norm_ppf(q): 

259 return sc.ndtri(q) 

260 

261 

262def _norm_sf(x): 

263 return _norm_cdf(-x) 

264 

265 

266def _norm_logsf(x): 

267 return _norm_logcdf(-x) 

268 

269 

270def _norm_isf(q): 

271 return -_norm_ppf(q) 

272 

273 

274class norm_gen(rv_continuous): 

275 r"""A normal continuous random variable. 

276 

277 The location (``loc``) keyword specifies the mean. 

278 The scale (``scale``) keyword specifies the standard deviation. 

279 

280 %(before_notes)s 

281 

282 Notes 

283 ----- 

284 The probability density function for `norm` is: 

285 

286 .. math:: 

287 

288 f(x) = \frac{\exp(-x^2/2)}{\sqrt{2\pi}} 

289 

290 for a real number :math:`x`. 

291 

292 %(after_notes)s 

293 

294 %(example)s 

295 

296 """ 

297 def _rvs(self, size=None, random_state=None): 

298 return random_state.standard_normal(size) 

299 

300 def _pdf(self, x): 

301 # norm.pdf(x) = exp(-x**2/2)/sqrt(2*pi) 

302 return _norm_pdf(x) 

303 

304 def _logpdf(self, x): 

305 return _norm_logpdf(x) 

306 

307 def _cdf(self, x): 

308 return _norm_cdf(x) 

309 

310 def _logcdf(self, x): 

311 return _norm_logcdf(x) 

312 

313 def _sf(self, x): 

314 return _norm_sf(x) 

315 

316 def _logsf(self, x): 

317 return _norm_logsf(x) 

318 

319 def _ppf(self, q): 

320 return _norm_ppf(q) 

321 

322 def _isf(self, q): 

323 return _norm_isf(q) 

324 

325 def _stats(self): 

326 return 0.0, 1.0, 0.0, 0.0 

327 

328 def _entropy(self): 

329 return 0.5*(np.log(2*np.pi)+1) 

330 

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) 

338 

339 _remove_optimizer_parameters(kwds) 

340 

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.") 

347 

348 data = np.asarray(data) 

349 

350 if not np.isfinite(data).all(): 

351 raise RuntimeError("The data contains non-finite values.") 

352 

353 if floc is None: 

354 loc = data.mean() 

355 else: 

356 loc = floc 

357 

358 if fscale is None: 

359 scale = np.sqrt(((data - loc)**2).mean()) 

360 else: 

361 scale = fscale 

362 

363 return loc, scale 

364 

365 def _munp(self, n): 

366 """ 

367 @returns Moments of standard normal distribution for integer n >= 0 

368 

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. 

375 

376 

377norm = norm_gen(name='norm') 

378 

379 

380class alpha_gen(rv_continuous): 

381 r"""An alpha continuous random variable. 

382 

383 %(before_notes)s 

384 

385 Notes 

386 ----- 

387 The probability density function for `alpha` ([1]_, [2]_) is: 

388 

389 .. math:: 

390 

391 f(x, a) = \frac{1}{x^2 \Phi(a) \sqrt{2\pi}} * 

392 \exp(-\frac{1}{2} (a-1/x)^2) 

393 

394 where :math:`\Phi` is the normal CDF, :math:`x > 0`, and :math:`a > 0`. 

395 

396 `alpha` takes ``a`` as a shape parameter. 

397 

398 %(after_notes)s 

399 

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). 

408 

409 %(example)s 

410 

411 """ 

412 _support_mask = rv_continuous._open_support_mask 

413 

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) 

417 

418 def _logpdf(self, x, a): 

419 return -2*np.log(x) + _norm_logpdf(a-1.0/x) - np.log(_norm_cdf(a)) 

420 

421 def _cdf(self, x, a): 

422 return _norm_cdf(a-1.0/x) / _norm_cdf(a) 

423 

424 def _ppf(self, q, a): 

425 return 1.0/np.asarray(a-sc.ndtri(q*_norm_cdf(a))) 

426 

427 def _stats(self, a): 

428 return [np.inf]*2 + [np.nan]*2 

429 

430 

431alpha = alpha_gen(a=0.0, name='alpha') 

432 

433 

434class anglit_gen(rv_continuous): 

435 r"""An anglit continuous random variable. 

436 

437 %(before_notes)s 

438 

439 Notes 

440 ----- 

441 The probability density function for `anglit` is: 

442 

443 .. math:: 

444 

445 f(x) = \sin(2x + \pi/2) = \cos(2x) 

446 

447 for :math:`-\pi/4 \le x \le \pi/4`. 

448 

449 %(after_notes)s 

450 

451 %(example)s 

452 

453 """ 

454 def _pdf(self, x): 

455 # anglit.pdf(x) = sin(2*x + \pi/2) = cos(2*x) 

456 return np.cos(2*x) 

457 

458 def _cdf(self, x): 

459 return np.sin(x+np.pi/4)**2.0 

460 

461 def _ppf(self, q): 

462 return np.arcsin(np.sqrt(q))-np.pi/4 

463 

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 

466 

467 def _entropy(self): 

468 return 1-np.log(2) 

469 

470 

471anglit = anglit_gen(a=-np.pi/4, b=np.pi/4, name='anglit') 

472 

473 

474class arcsine_gen(rv_continuous): 

475 r"""An arcsine continuous random variable. 

476 

477 %(before_notes)s 

478 

479 Notes 

480 ----- 

481 The probability density function for `arcsine` is: 

482 

483 .. math:: 

484 

485 f(x) = \frac{1}{\pi \sqrt{x (1-x)}} 

486 

487 for :math:`0 < x < 1`. 

488 

489 %(after_notes)s 

490 

491 %(example)s 

492 

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)) 

497 

498 def _cdf(self, x): 

499 return 2.0/np.pi*np.arcsin(np.sqrt(x)) 

500 

501 def _ppf(self, q): 

502 return np.sin(np.pi/2.0*q)**2.0 

503 

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 

510 

511 def _entropy(self): 

512 return -0.24156447527049044468 

513 

514 

515arcsine = arcsine_gen(a=0.0, b=1.0, name='arcsine') 

516 

517 

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 ) 

529 

530 

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,) 

538 

539 

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 

547 

548 

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 

561 

562 

563class beta_gen(rv_continuous): 

564 r"""A beta continuous random variable. 

565 

566 %(before_notes)s 

567 

568 Notes 

569 ----- 

570 The probability density function for `beta` is: 

571 

572 .. math:: 

573 

574 f(x, a, b) = \frac{\Gamma(a+b) x^{a-1} (1-x)^{b-1}} 

575 {\Gamma(a) \Gamma(b)} 

576 

577 for :math:`0 <= x <= 1`, :math:`a > 0`, :math:`b > 0`, where 

578 :math:`\Gamma` is the gamma function (`scipy.special.gamma`). 

579 

580 `beta` takes :math:`a` and :math:`b` as shape parameters. 

581 

582 %(after_notes)s 

583 

584 %(example)s 

585 

586 """ 

587 def _rvs(self, a, b, size=None, random_state=None): 

588 return random_state.beta(a, b, size) 

589 

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)) 

595 

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 

600 

601 def _cdf(self, x, a, b): 

602 return sc.btdtr(a, b, x) 

603 

604 def _ppf(self, q, a, b): 

605 return sc.btdtri(a, b, q) 

606 

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 

614 

615 def _fitstart(self, data): 

616 g1 = _skew(data) 

617 g2 = _kurtosis(data) 

618 

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)) 

628 

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. 

636 

637 floc = kwds.get('floc', None) 

638 fscale = kwds.get('fscale', None) 

639 

640 if floc is None or fscale is None: 

641 # do general fit 

642 return super(beta_gen, self).fit(data, *args, **kwds) 

643 

644 # We already got these from kwds, so just pop them. 

645 kwds.pop('floc', None) 

646 kwds.pop('fscale', None) 

647 

648 f0 = _get_fixed_fit_value(kwds, ['f0', 'fa', 'fix_a']) 

649 f1 = _get_fixed_fit_value(kwds, ['f1', 'fb', 'fix_b']) 

650 

651 _remove_optimizer_parameters(kwds) 

652 

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.") 

657 

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.) 

663 

664 if not np.isfinite(data).all(): 

665 raise RuntimeError("The data contains non-finite values.") 

666 

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) 

671 

672 xbar = data.mean() 

673 

674 if f0 is not None or f1 is not None: 

675 # One of the shape parameters is fixed. 

676 

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 

686 

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) 

692 

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] 

702 

703 if f0 is not None: 

704 # The shape parameter a was fixed, so swap back the 

705 # parameters. 

706 a, b = b, a 

707 

708 else: 

709 # Neither of the shape parameters is fixed. 

710 

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() 

715 

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 

721 

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 

731 

732 return a, b, floc, fscale 

733 

734 

735beta = beta_gen(a=0.0, b=1.0, name='beta') 

736 

737 

738class betaprime_gen(rv_continuous): 

739 r"""A beta prime continuous random variable. 

740 

741 %(before_notes)s 

742 

743 Notes 

744 ----- 

745 The probability density function for `betaprime` is: 

746 

747 .. math:: 

748 

749 f(x, a, b) = \frac{x^{a-1} (1+x)^{-a-b}}{\beta(a, b)} 

750 

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`). 

753 

754 `betaprime` takes ``a`` and ``b`` as shape parameters. 

755 

756 %(after_notes)s 

757 

758 %(example)s 

759 

760 """ 

761 _support_mask = rv_continuous._open_support_mask 

762 

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 

767 

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)) 

771 

772 def _logpdf(self, x, a, b): 

773 return sc.xlogy(a - 1.0, x) - sc.xlog1py(a + b, x) - sc.betaln(a, b) 

774 

775 def _cdf(self, x, a, b): 

776 return sc.betainc(a, b, x/(1.+x)) 

777 

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 

798 

799 

800betaprime = betaprime_gen(a=0.0, name='betaprime') 

801 

802 

803class bradford_gen(rv_continuous): 

804 r"""A Bradford continuous random variable. 

805 

806 %(before_notes)s 

807 

808 Notes 

809 ----- 

810 The probability density function for `bradford` is: 

811 

812 .. math:: 

813 

814 f(x, c) = \frac{c}{\log(1+c) (1+cx)} 

815 

816 for :math:`0 <= x <= 1` and :math:`c > 0`. 

817 

818 `bradford` takes ``c`` as a shape parameter for :math:`c`. 

819 

820 %(after_notes)s 

821 

822 %(example)s 

823 

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) 

828 

829 def _cdf(self, x, c): 

830 return sc.log1p(c*x) / sc.log1p(c) 

831 

832 def _ppf(self, q, c): 

833 return sc.expm1(q * sc.log1p(c)) / c 

834 

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 

849 

850 def _entropy(self, c): 

851 k = np.log(1+c) 

852 return k/2.0 - np.log(c/k) 

853 

854 

855bradford = bradford_gen(a=0.0, b=1.0, name='bradford') 

856 

857 

858class burr_gen(rv_continuous): 

859 r"""A Burr (Type III) continuous random variable. 

860 

861 %(before_notes)s 

862 

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 

868 

869 Notes 

870 ----- 

871 The probability density function for `burr` is: 

872 

873 .. math:: 

874 

875 f(x, c, d) = c d x^{-c - 1} / (1 + x^{-c})^{d + 1} 

876 

877 for :math:`x >= 0` and :math:`c, d > 0`. 

878 

879 `burr` takes :math:`c` and :math:`d` as shape parameters. 

880 

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`. 

887 

888 %(after_notes)s 

889 

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). 

897 

898 %(example)s 

899 

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. 

904 

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 

914 

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 

926 

927 def _cdf(self, x, c, d): 

928 return (1 + x**(-c))**(-d) 

929 

930 def _logcdf(self, x, c, d): 

931 return sc.log1p(x**(-c)) * (-d) 

932 

933 def _sf(self, x, c, d): 

934 return np.exp(self._logsf(x, c, d)) 

935 

936 def _logsf(self, x, c, d): 

937 return np.log1p(- (1 + x**(-c))**(-d)) 

938 

939 def _ppf(self, q, c, d): 

940 return (q**(-1.0/d) - 1)**(-1.0/c) 

941 

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 

961 

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) 

970 

971 

972burr = burr_gen(a=0.0, name='burr') 

973 

974 

975class burr12_gen(rv_continuous): 

976 r"""A Burr (Type XII) continuous random variable. 

977 

978 %(before_notes)s 

979 

980 See Also 

981 -------- 

982 fisk : a special case of either `burr` or `burr12` with ``d=1`` 

983 burr : Burr Type III distribution 

984 

985 Notes 

986 ----- 

987 The probability density function for `burr` is: 

988 

989 .. math:: 

990 

991 f(x, c, d) = c d x^{c-1} / (1 + x^c)^{d + 1} 

992 

993 for :math:`x >= 0` and :math:`c, d > 0`. 

994 

995 `burr12` takes ``c`` and ``d`` as shape parameters for :math:`c` 

996 and :math:`d`. 

997 

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]_. 

1000 

1001 %(after_notes)s 

1002 

1003 The Burr type 12 distribution is also sometimes referred to as 

1004 the Singh-Maddala distribution from NIST [2]_. 

1005 

1006 References 

1007 ---------- 

1008 .. [1] Burr, I. W. "Cumulative frequency functions", Annals of 

1009 Mathematical Statistics, 13(2), pp 215-232 (1942). 

1010 

1011 .. [2] https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/b12pdf.htm 

1012 

1013 .. [3] "Burr distribution", 

1014 https://en.wikipedia.org/wiki/Burr_distribution 

1015 

1016 %(example)s 

1017 

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)) 

1022 

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) 

1025 

1026 def _cdf(self, x, c, d): 

1027 return -sc.expm1(self._logsf(x, c, d)) 

1028 

1029 def _logcdf(self, x, c, d): 

1030 return sc.log1p(-(1 + x**c)**(-d)) 

1031 

1032 def _sf(self, x, c, d): 

1033 return np.exp(self._logsf(x, c, d)) 

1034 

1035 def _logsf(self, x, c, d): 

1036 return sc.xlog1py(-d, x**c) 

1037 

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) 

1043 

1044 def _munp(self, n, c, d): 

1045 nc = 1. * n / c 

1046 return d * sc.beta(1.0 + nc, d - nc) 

1047 

1048 

1049burr12 = burr12_gen(a=0.0, name='burr12') 

1050 

1051 

1052class fisk_gen(burr_gen): 

1053 r"""A Fisk continuous random variable. 

1054 

1055 The Fisk distribution is also known as the log-logistic distribution. 

1056 

1057 %(before_notes)s 

1058 

1059 Notes 

1060 ----- 

1061 The probability density function for `fisk` is: 

1062 

1063 .. math:: 

1064 

1065 f(x, c) = c x^{-c-1} (1 + x^{-c})^{-2} 

1066 

1067 for :math:`x >= 0` and :math:`c > 0`. 

1068 

1069 `fisk` takes ``c`` as a shape parameter for :math:`c`. 

1070 

1071 `fisk` is a special case of `burr` or `burr12` with ``d=1``. 

1072 

1073 %(after_notes)s 

1074 

1075 See Also 

1076 -------- 

1077 burr 

1078 

1079 %(example)s 

1080 

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) 

1085 

1086 def _cdf(self, x, c): 

1087 return burr._cdf(x, c, 1.0) 

1088 

1089 def _sf(self, x, c): 

1090 return burr._sf(x, c, 1.0) 

1091 

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) 

1095 

1096 def _logcdf(self, x, c): 

1097 return burr._logcdf(x, c, 1.0) 

1098 

1099 def _logsf(self, x, c): 

1100 return burr._logsf(x, c, 1.0) 

1101 

1102 def _ppf(self, x, c): 

1103 return burr._ppf(x, c, 1.0) 

1104 

1105 def _munp(self, n, c): 

1106 return burr._munp(n, c, 1.0) 

1107 

1108 def _stats(self, c): 

1109 return burr._stats(c, 1.0) 

1110 

1111 def _entropy(self, c): 

1112 return 2 - np.log(c) 

1113 

1114 

1115fisk = fisk_gen(a=0.0, name='fisk') 

1116 

1117 

1118# median = loc 

1119class cauchy_gen(rv_continuous): 

1120 r"""A Cauchy continuous random variable. 

1121 

1122 %(before_notes)s 

1123 

1124 Notes 

1125 ----- 

1126 The probability density function for `cauchy` is 

1127 

1128 .. math:: 

1129 

1130 f(x) = \frac{1}{\pi (1 + x^2)} 

1131 

1132 for a real number :math:`x`. 

1133 

1134 %(after_notes)s 

1135 

1136 %(example)s 

1137 

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) 

1142 

1143 def _cdf(self, x): 

1144 return 0.5 + 1.0/np.pi*np.arctan(x) 

1145 

1146 def _ppf(self, q): 

1147 return np.tan(np.pi*q-np.pi/2.0) 

1148 

1149 def _sf(self, x): 

1150 return 0.5 - 1.0/np.pi*np.arctan(x) 

1151 

1152 def _isf(self, q): 

1153 return np.tan(np.pi/2.0-np.pi*q) 

1154 

1155 def _stats(self): 

1156 return np.nan, np.nan, np.nan, np.nan 

1157 

1158 def _entropy(self): 

1159 return np.log(4*np.pi) 

1160 

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 

1165 

1166 

1167cauchy = cauchy_gen(name='cauchy') 

1168 

1169 

1170class chi_gen(rv_continuous): 

1171 r"""A chi continuous random variable. 

1172 

1173 %(before_notes)s 

1174 

1175 Notes 

1176 ----- 

1177 The probability density function for `chi` is: 

1178 

1179 .. math:: 

1180 

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) 

1183 

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`). 

1187 

1188 Special cases of `chi` are: 

1189 

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` 

1193 

1194 `chi` takes ``df`` as a shape parameter. 

1195 

1196 %(after_notes)s 

1197 

1198 %(example)s 

1199 

1200 """ 

1201 

1202 def _rvs(self, df, size=None, random_state=None): 

1203 return np.sqrt(chi2.rvs(df, size=size, random_state=random_state)) 

1204 

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)) 

1210 

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 

1214 

1215 def _cdf(self, x, df): 

1216 return sc.gammainc(.5*df, .5*x**2) 

1217 

1218 def _ppf(self, q, df): 

1219 return np.sqrt(2*sc.gammaincinv(.5*df, q)) 

1220 

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 

1228 

1229 

1230chi = chi_gen(a=0.0, name='chi') 

1231 

1232 

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. 

1236 

1237 %(before_notes)s 

1238 

1239 Notes 

1240 ----- 

1241 The probability density function for `chi2` is: 

1242 

1243 .. math:: 

1244 

1245 f(x, k) = \frac{1}{2^{k/2} \Gamma \left( k/2 \right)} 

1246 x^{k/2-1} \exp \left( -x/2 \right) 

1247 

1248 for :math:`x > 0` and :math:`k > 0` (degrees of freedom, denoted ``df`` 

1249 in the implementation). 

1250 

1251 `chi2` takes ``df`` as a shape parameter. 

1252 

1253 %(after_notes)s 

1254 

1255 %(example)s 

1256 

1257 """ 

1258 def _rvs(self, df, size=None, random_state=None): 

1259 return random_state.chisquare(df, size) 

1260 

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)) 

1264 

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. 

1267 

1268 def _cdf(self, x, df): 

1269 return sc.chdtr(df, x) 

1270 

1271 def _sf(self, x, df): 

1272 return sc.chdtrc(df, x) 

1273 

1274 def _isf(self, p, df): 

1275 return sc.chdtri(df, p) 

1276 

1277 def _ppf(self, p, df): 

1278 return 2*sc.gammaincinv(df/2, p) 

1279 

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 

1286 

1287 

1288chi2 = chi2_gen(a=0.0, name='chi2') 

1289 

1290 

1291class cosine_gen(rv_continuous): 

1292 r"""A cosine continuous random variable. 

1293 

1294 %(before_notes)s 

1295 

1296 Notes 

1297 ----- 

1298 The cosine distribution is an approximation to the normal distribution. 

1299 The probability density function for `cosine` is: 

1300 

1301 .. math:: 

1302 

1303 f(x) = \frac{1}{2\pi} (1+\cos(x)) 

1304 

1305 for :math:`-\pi \le x \le \pi`. 

1306 

1307 %(after_notes)s 

1308 

1309 %(example)s 

1310 

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)) 

1315 

1316 def _cdf(self, x): 

1317 return 1.0/2/np.pi*(np.pi + x + np.sin(x)) 

1318 

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) 

1321 

1322 def _entropy(self): 

1323 return np.log(4*np.pi)-1.0 

1324 

1325 

1326cosine = cosine_gen(a=-np.pi, b=np.pi, name='cosine') 

1327 

1328 

1329class dgamma_gen(rv_continuous): 

1330 r"""A double gamma continuous random variable. 

1331 

1332 %(before_notes)s 

1333 

1334 Notes 

1335 ----- 

1336 The probability density function for `dgamma` is: 

1337 

1338 .. math:: 

1339 

1340 f(x, a) = \frac{1}{2\Gamma(a)} |x|^{a-1} \exp(-|x|) 

1341 

1342 for a real number :math:`x` and :math:`a > 0`. :math:`\Gamma` is the 

1343 gamma function (`scipy.special.gamma`). 

1344 

1345 `dgamma` takes ``a`` as a shape parameter for :math:`a`. 

1346 

1347 %(after_notes)s 

1348 

1349 %(example)s 

1350 

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) 

1356 

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) 

1361 

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) 

1365 

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) 

1369 

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) 

1373 

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) 

1377 

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 

1381 

1382 

1383dgamma = dgamma_gen(name='dgamma') 

1384 

1385 

1386class dweibull_gen(rv_continuous): 

1387 r"""A double Weibull continuous random variable. 

1388 

1389 %(before_notes)s 

1390 

1391 Notes 

1392 ----- 

1393 The probability density function for `dweibull` is given by 

1394 

1395 .. math:: 

1396 

1397 f(x, c) = c / 2 |x|^{c-1} \exp(-|x|^c) 

1398 

1399 for a real number :math:`x` and :math:`c > 0`. 

1400 

1401 `dweibull` takes ``c`` as a shape parameter for :math:`c`. 

1402 

1403 %(after_notes)s 

1404 

1405 %(example)s 

1406 

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)) 

1412 

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 

1418 

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 

1422 

1423 def _cdf(self, x, c): 

1424 Cx1 = 0.5 * np.exp(-abs(x)**c) 

1425 return np.where(x > 0, 1 - Cx1, Cx1) 

1426 

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) 

1431 

1432 def _munp(self, n, c): 

1433 return (1 - (n % 2)) * sc.gamma(1.0 + 1.0 * n / c) 

1434 

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 

1440 

1441 

1442dweibull = dweibull_gen(name='dweibull') 

1443 

1444 

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. 

1448 

1449 %(before_notes)s 

1450 

1451 Notes 

1452 ----- 

1453 The probability density function for `expon` is: 

1454 

1455 .. math:: 

1456 

1457 f(x) = \exp(-x) 

1458 

1459 for :math:`x \ge 0`. 

1460 

1461 %(after_notes)s 

1462 

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``. 

1466 

1467 %(example)s 

1468 

1469 """ 

1470 def _rvs(self, size=None, random_state=None): 

1471 return random_state.standard_exponential(size) 

1472 

1473 def _pdf(self, x): 

1474 # expon.pdf(x) = exp(-x) 

1475 return np.exp(-x) 

1476 

1477 def _logpdf(self, x): 

1478 return -x 

1479 

1480 def _cdf(self, x): 

1481 return -sc.expm1(-x) 

1482 

1483 def _ppf(self, q): 

1484 return -sc.log1p(-q) 

1485 

1486 def _sf(self, x): 

1487 return np.exp(-x) 

1488 

1489 def _logsf(self, x): 

1490 return -x 

1491 

1492 def _isf(self, q): 

1493 return -np.log(q) 

1494 

1495 def _stats(self): 

1496 return 1.0, 1.0, 2.0, 6.0 

1497 

1498 def _entropy(self): 

1499 return 1.0 

1500 

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.") 

1508 

1509 floc = kwds.pop('floc', None) 

1510 fscale = kwds.pop('fscale', None) 

1511 

1512 _remove_optimizer_parameters(kwds) 

1513 

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.") 

1518 

1519 data = np.asarray(data) 

1520 

1521 if not np.isfinite(data).all(): 

1522 raise RuntimeError("The data contains non-finite values.") 

1523 

1524 data_min = data.min() 

1525 

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) 

1534 

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 

1540 

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) 

1544 

1545 

1546expon = expon_gen(a=0.0, name='expon') 

1547 

1548 

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. 

1554 

1555 %(before_notes)s 

1556 

1557 Notes 

1558 ----- 

1559 The probability density function for `exponnorm` is: 

1560 

1561 .. math:: 

1562 

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) 

1565 

1566 where :math:`x` is a real number and :math:`K > 0`. 

1567 

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``. 

1571 

1572 %(after_notes)s 

1573 

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)`. 

1581 

1582 .. versionadded:: 0.16.0 

1583 

1584 %(example)s 

1585 

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 

1591 

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))) 

1601 

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))) 

1606 

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) 

1611 

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) 

1616 

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 

1623 

1624 

1625exponnorm = exponnorm_gen(name='exponnorm') 

1626 

1627 

1628class exponweib_gen(rv_continuous): 

1629 r"""An exponentiated Weibull continuous random variable. 

1630 

1631 %(before_notes)s 

1632 

1633 See Also 

1634 -------- 

1635 weibull_min, numpy.random.RandomState.weibull 

1636 

1637 Notes 

1638 ----- 

1639 The probability density function for `exponweib` is: 

1640 

1641 .. math:: 

1642 

1643 f(x, a, c) = a c [1-\exp(-x^c)]^{a-1} \exp(-x^c) x^{c-1} 

1644 

1645 and its cumulative distribution function is: 

1646 

1647 .. math:: 

1648 

1649 F(x, a, c) = [1-\exp(-x^c)]^a 

1650 

1651 for :math:`x > 0`, :math:`a > 0`, :math:`c > 0`. 

1652 

1653 `exponweib` takes :math:`a` and :math:`c` as shape parameters: 

1654 

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. 

1659 

1660 %(after_notes)s 

1661 

1662 References 

1663 ---------- 

1664 https://en.wikipedia.org/wiki/Exponentiated_Weibull_distribution 

1665 

1666 %(example)s 

1667 

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)) 

1673 

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 

1680 

1681 def _cdf(self, x, a, c): 

1682 exm1c = -sc.expm1(-x**c) 

1683 return exm1c**a 

1684 

1685 def _ppf(self, q, a, c): 

1686 return (-sc.log1p(-q**(1.0/a)))**np.asarray(1.0/c) 

1687 

1688 

1689exponweib = exponweib_gen(a=0.0, name='exponweib') 

1690 

1691 

1692class exponpow_gen(rv_continuous): 

1693 r"""An exponential power continuous random variable. 

1694 

1695 %(before_notes)s 

1696 

1697 Notes 

1698 ----- 

1699 The probability density function for `exponpow` is: 

1700 

1701 .. math:: 

1702 

1703 f(x, b) = b x^{b-1} \exp(1 + x^b - \exp(x^b)) 

1704 

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". 

1708 

1709 `exponpow` takes ``b`` as a shape parameter for :math:`b`. 

1710 

1711 %(after_notes)s 

1712 

1713 References 

1714 ---------- 

1715 http://www.math.wm.edu/~leemis/chart/UDR/PDFs/Exponentialpower.pdf 

1716 

1717 %(example)s 

1718 

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)) 

1723 

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 

1728 

1729 def _cdf(self, x, b): 

1730 return -sc.expm1(-sc.expm1(x**b)) 

1731 

1732 def _sf(self, x, b): 

1733 return np.exp(-sc.expm1(x**b)) 

1734 

1735 def _isf(self, x, b): 

1736 return (sc.log1p(-np.log(x)))**(1./b) 

1737 

1738 def _ppf(self, q, b): 

1739 return pow(sc.log1p(-sc.log1p(-q)), 1.0/b) 

1740 

1741 

1742exponpow = exponpow_gen(a=0.0, name='exponpow') 

1743 

1744 

1745class fatiguelife_gen(rv_continuous): 

1746 r"""A fatigue-life (Birnbaum-Saunders) continuous random variable. 

1747 

1748 %(before_notes)s 

1749 

1750 Notes 

1751 ----- 

1752 The probability density function for `fatiguelife` is: 

1753 

1754 .. math:: 

1755 

1756 f(x, c) = \frac{x+1}{2c\sqrt{2\pi x^3}} \exp(-\frac{(x-1)^2}{2x c^2}) 

1757 

1758 for :math:`x >= 0` and :math:`c > 0`. 

1759 

1760 `fatiguelife` takes ``c`` as a shape parameter for :math:`c`. 

1761 

1762 %(after_notes)s 

1763 

1764 References 

1765 ---------- 

1766 .. [1] "Birnbaum-Saunders distribution", 

1767 https://en.wikipedia.org/wiki/Birnbaum-Saunders_distribution 

1768 

1769 %(example)s 

1770 

1771 """ 

1772 _support_mask = rv_continuous._open_support_mask 

1773 

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 

1780 

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)) 

1785 

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))) 

1789 

1790 def _cdf(self, x, c): 

1791 return _norm_cdf(1.0 / c * (np.sqrt(x) - 1.0/np.sqrt(x))) 

1792 

1793 def _ppf(self, q, c): 

1794 tmp = c*sc.ndtri(q) 

1795 return 0.25 * (tmp + np.sqrt(tmp**2 + 4))**2 

1796 

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 

1810 

1811 

1812fatiguelife = fatiguelife_gen(a=0.0, name='fatiguelife') 

1813 

1814 

1815class foldcauchy_gen(rv_continuous): 

1816 r"""A folded Cauchy continuous random variable. 

1817 

1818 %(before_notes)s 

1819 

1820 Notes 

1821 ----- 

1822 The probability density function for `foldcauchy` is: 

1823 

1824 .. math:: 

1825 

1826 f(x, c) = \frac{1}{\pi (1+(x-c)^2)} + \frac{1}{\pi (1+(x+c)^2)} 

1827 

1828 for :math:`x \ge 0`. 

1829 

1830 `foldcauchy` takes ``c`` as a shape parameter for :math:`c`. 

1831 

1832 %(example)s 

1833 

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)) 

1838 

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)) 

1842 

1843 def _cdf(self, x, c): 

1844 return 1.0/np.pi*(np.arctan(x-c) + np.arctan(x+c)) 

1845 

1846 def _stats(self, c): 

1847 return np.inf, np.inf, np.nan, np.nan 

1848 

1849 

1850foldcauchy = foldcauchy_gen(a=0.0, name='foldcauchy') 

1851 

1852 

1853class f_gen(rv_continuous): 

1854 r"""An F continuous random variable. 

1855 

1856 %(before_notes)s 

1857 

1858 Notes 

1859 ----- 

1860 The probability density function for `f` is: 

1861 

1862 .. math:: 

1863 

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)} 

1867 

1868 for :math:`x > 0`. 

1869 

1870 `f` takes ``dfn`` and ``dfd`` as shape parameters. 

1871 

1872 %(after_notes)s 

1873 

1874 %(example)s 

1875 

1876 """ 

1877 def _rvs(self, dfn, dfd, size=None, random_state=None): 

1878 return random_state.f(dfn, dfd, size) 

1879 

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)) 

1885 

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 

1892 

1893 def _cdf(self, x, dfn, dfd): 

1894 return sc.fdtr(dfn, dfd, x) 

1895 

1896 def _sf(self, x, dfn, dfd): 

1897 return sc.fdtrc(dfn, dfd, x) 

1898 

1899 def _ppf(self, q, dfn, dfd): 

1900 return sc.fdtri(dfn, dfd, q) 

1901 

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. 

1905 

1906 mu = _lazywhere( 

1907 v2 > 2, (v2, v2_2), 

1908 lambda v2, v2_2: v2 / v2_2, 

1909 np.inf) 

1910 

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) 

1916 

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.) 

1923 

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. 

1929 

1930 return mu, mu2, g1, g2 

1931 

1932 

1933f = f_gen(a=0.0, name='f') 

1934 

1935 

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 

1941 

1942## Half-normal is folded normal with shape-parameter c=0. 

1943 

1944class foldnorm_gen(rv_continuous): 

1945 r"""A folded normal continuous random variable. 

1946 

1947 %(before_notes)s 

1948 

1949 Notes 

1950 ----- 

1951 The probability density function for `foldnorm` is: 

1952 

1953 .. math:: 

1954 

1955 f(x, c) = \sqrt{2/\pi} cosh(c x) \exp(-\frac{x^2+c^2}{2}) 

1956 

1957 for :math:`c \ge 0`. 

1958 

1959 `foldnorm` takes ``c`` as a shape parameter for :math:`c`. 

1960 

1961 %(after_notes)s 

1962 

1963 %(example)s 

1964 

1965 """ 

1966 def _argcheck(self, c): 

1967 return c >= 0 

1968 

1969 def _rvs(self, c, size=None, random_state=None): 

1970 return abs(random_state.standard_normal(size) + c) 

1971 

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) 

1975 

1976 def _cdf(self, x, c): 

1977 return _norm_cdf(x-c) + _norm_cdf(x+c) - 1.0 

1978 

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) 

1985 

1986 mu = 2.*expfac + c * sc.erf(c/np.sqrt(2)) 

1987 mu2 = c2 + 1 - mu*mu 

1988 

1989 g1 = 2. * (mu*mu*mu - c2*mu - expfac) 

1990 g1 /= np.power(mu2, 1.5) 

1991 

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. 

1995 

1996 return mu, mu2, g1, g2 

1997 

1998 

1999foldnorm = foldnorm_gen(a=0.0, name='foldnorm') 

2000 

2001 

2002class weibull_min_gen(rv_continuous): 

2003 r"""Weibull minimum continuous random variable. 

2004 

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. 

2009 

2010 %(before_notes)s 

2011 

2012 See Also 

2013 -------- 

2014 weibull_max, numpy.random.RandomState.weibull, exponweib 

2015 

2016 Notes 

2017 ----- 

2018 The probability density function for `weibull_min` is: 

2019 

2020 .. math:: 

2021 

2022 f(x, c) = c x^{c-1} \exp(-x^c) 

2023 

2024 for :math:`x > 0`, :math:`c > 0`. 

2025 

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. 

2031 

2032 %(after_notes)s 

2033 

2034 References 

2035 ---------- 

2036 https://en.wikipedia.org/wiki/Weibull_distribution 

2037 

2038 https://en.wikipedia.org/wiki/Fisher-Tippett-Gnedenko_theorem 

2039 

2040 %(example)s 

2041 

2042 """ 

2043 

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)) 

2047 

2048 def _logpdf(self, x, c): 

2049 return np.log(c) + sc.xlogy(c - 1, x) - pow(x, c) 

2050 

2051 def _cdf(self, x, c): 

2052 return -sc.expm1(-pow(x, c)) 

2053 

2054 def _sf(self, x, c): 

2055 return np.exp(-pow(x, c)) 

2056 

2057 def _logsf(self, x, c): 

2058 return -pow(x, c) 

2059 

2060 def _ppf(self, q, c): 

2061 return pow(-sc.log1p(-q), 1.0/c) 

2062 

2063 def _munp(self, n, c): 

2064 return sc.gamma(1.0+n*1.0/c) 

2065 

2066 def _entropy(self, c): 

2067 return -_EULER / c - np.log(c) + _EULER + 1 

2068 

2069 

2070weibull_min = weibull_min_gen(a=0.0, name='weibull_min') 

2071 

2072 

2073class weibull_max_gen(rv_continuous): 

2074 r"""Weibull maximum continuous random variable. 

2075 

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. 

2080 

2081 %(before_notes)s 

2082 

2083 See Also 

2084 -------- 

2085 weibull_min 

2086 

2087 Notes 

2088 ----- 

2089 The probability density function for `weibull_max` is: 

2090 

2091 .. math:: 

2092 

2093 f(x, c) = c (-x)^{c-1} \exp(-(-x)^c) 

2094 

2095 for :math:`x < 0`, :math:`c > 0`. 

2096 

2097 `weibull_max` takes ``c`` as a shape parameter for :math:`c`. 

2098 

2099 %(after_notes)s 

2100 

2101 References 

2102 ---------- 

2103 https://en.wikipedia.org/wiki/Weibull_distribution 

2104 

2105 https://en.wikipedia.org/wiki/Fisher-Tippett-Gnedenko_theorem 

2106 

2107 %(example)s 

2108 

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)) 

2113 

2114 def _logpdf(self, x, c): 

2115 return np.log(c) + sc.xlogy(c-1, -x) - pow(-x, c) 

2116 

2117 def _cdf(self, x, c): 

2118 return np.exp(-pow(-x, c)) 

2119 

2120 def _logcdf(self, x, c): 

2121 return -pow(-x, c) 

2122 

2123 def _sf(self, x, c): 

2124 return -sc.expm1(-pow(-x, c)) 

2125 

2126 def _ppf(self, q, c): 

2127 return -pow(-np.log(q), 1.0/c) 

2128 

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 

2136 

2137 def _entropy(self, c): 

2138 return -_EULER / c - np.log(c) + _EULER + 1 

2139 

2140 

2141weibull_max = weibull_max_gen(b=0.0, name='weibull_max') 

2142 

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'] 

2147 

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`.""" 

2154 

2155 

2156class frechet_r_gen(weibull_min_gen): 

2157 """A Frechet right (or Weibull minimum) continuous random variable. 

2158 

2159 %(before_notes)s 

2160 

2161 See Also 

2162 -------- 

2163 weibull_min : The same distribution as `frechet_r`. 

2164 

2165 Notes 

2166 ----- 

2167 %(after_notes)s 

2168 

2169 %(example)s 

2170 """ 

2171 

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) 

2175 

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) 

2179 

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) 

2183 

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) 

2187 

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) 

2191 

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) 

2195 

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) 

2199 

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) 

2203 

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) 

2207 

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) 

2211 

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) 

2215 

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) 

2219 

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) 

2223 

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) 

2227 

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) 

2231 

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) 

2235 

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) 

2239 

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) 

2243 

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) 

2247 

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) 

2251 

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) 

2255 

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) 

2259 

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) 

2263 

2264 

2265frechet_r = frechet_r_gen(a=0.0, name='frechet_r') 

2266 

2267 

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`.""" 

2274 

2275 

2276class frechet_l_gen(weibull_max_gen): 

2277 """A Frechet left (or Weibull maximum) continuous random variable. 

2278 

2279 %(before_notes)s 

2280 

2281 See Also 

2282 -------- 

2283 weibull_max : The same distribution as `frechet_l`. 

2284 

2285 Notes 

2286 ----- 

2287 %(after_notes)s 

2288 

2289 %(example)s 

2290 """ 

2291 

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) 

2295 

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) 

2299 

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) 

2303 

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) 

2307 

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) 

2311 

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) 

2315 

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) 

2319 

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) 

2323 

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) 

2327 

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) 

2331 

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) 

2335 

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) 

2339 

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) 

2343 

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) 

2347 

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) 

2351 

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) 

2355 

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) 

2359 

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) 

2363 

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) 

2367 

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) 

2371 

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) 

2375 

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) 

2379 

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) 

2383 

2384 

2385frechet_l = frechet_l_gen(b=0.0, name='frechet_l') 

2386 

2387 

2388class genlogistic_gen(rv_continuous): 

2389 r"""A generalized logistic continuous random variable. 

2390 

2391 %(before_notes)s 

2392 

2393 Notes 

2394 ----- 

2395 The probability density function for `genlogistic` is: 

2396 

2397 .. math:: 

2398 

2399 f(x, c) = c \frac{\exp(-x)} 

2400 {(1 + \exp(-x))^{c+1}} 

2401 

2402 for :math:`x >= 0`, :math:`c > 0`. 

2403 

2404 `genlogistic` takes ``c`` as a shape parameter for :math:`c`. 

2405 

2406 %(after_notes)s 

2407 

2408 %(example)s 

2409 

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)) 

2414 

2415 def _logpdf(self, x, c): 

2416 return np.log(c) - x - (c+1.0)*sc.log1p(np.exp(-x)) 

2417 

2418 def _cdf(self, x, c): 

2419 Cx = (1+np.exp(-x))**(-c) 

2420 return Cx 

2421 

2422 def _ppf(self, q, c): 

2423 vals = -np.log(pow(q, -1.0/c)-1) 

2424 return vals 

2425 

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 

2434 

2435 

2436genlogistic = genlogistic_gen(name='genlogistic') 

2437 

2438 

2439class genpareto_gen(rv_continuous): 

2440 r"""A generalized Pareto continuous random variable. 

2441 

2442 %(before_notes)s 

2443 

2444 Notes 

2445 ----- 

2446 The probability density function for `genpareto` is: 

2447 

2448 .. math:: 

2449 

2450 f(x, c) = (1 + c x)^{-1 - 1/c} 

2451 

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`. 

2454 

2455 `genpareto` takes ``c`` as a shape parameter for :math:`c`. 

2456 

2457 For :math:`c=0`, `genpareto` reduces to the exponential 

2458 distribution, `expon`: 

2459 

2460 .. math:: 

2461 

2462 f(x, 0) = \exp(-x) 

2463 

2464 For :math:`c=-1`, `genpareto` is uniform on ``[0, 1]``: 

2465 

2466 .. math:: 

2467 

2468 f(x, -1) = 1 

2469 

2470 %(after_notes)s 

2471 

2472 %(example)s 

2473 

2474 """ 

2475 def _argcheck(self, c): 

2476 return np.isfinite(c) 

2477 

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 

2485 

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)) 

2489 

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) 

2494 

2495 def _cdf(self, x, c): 

2496 return -sc.inv_boxcox1p(-x, -c) 

2497 

2498 def _sf(self, x, c): 

2499 return sc.inv_boxcox(-x, -c) 

2500 

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) 

2505 

2506 def _ppf(self, q, c): 

2507 return -sc.boxcox1p(-q, -c) 

2508 

2509 def _isf(self, q, c): 

2510 return -sc.boxcox(q, -c) 

2511 

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 

2540 

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)) 

2551 

2552 def _entropy(self, c): 

2553 return 1. + c 

2554 

2555 

2556genpareto = genpareto_gen(a=0.0, name='genpareto') 

2557 

2558 

2559class genexpon_gen(rv_continuous): 

2560 r"""A generalized exponential continuous random variable. 

2561 

2562 %(before_notes)s 

2563 

2564 Notes 

2565 ----- 

2566 The probability density function for `genexpon` is: 

2567 

2568 .. math:: 

2569 

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))) 

2572 

2573 for :math:`x \ge 0`, :math:`a, b, c > 0`. 

2574 

2575 `genexpon` takes :math:`a`, :math:`b` and :math:`c` as shape parameters. 

2576 

2577 %(after_notes)s 

2578 

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. 

2583 

2584 N. Balakrishnan, "The Exponential Distribution: Theory, Methods and 

2585 Applications", Asit P. Basu. 

2586 

2587 %(example)s 

2588 

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) 

2595 

2596 def _cdf(self, x, a, b, c): 

2597 return -sc.expm1((-a-b)*x + b*(-sc.expm1(-c*x))/c) 

2598 

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 

2601 

2602 

2603genexpon = genexpon_gen(a=0.0, name='genexpon') 

2604 

2605 

2606class genextreme_gen(rv_continuous): 

2607 r"""A generalized extreme value continuous random variable. 

2608 

2609 %(before_notes)s 

2610 

2611 See Also 

2612 -------- 

2613 gumbel_r 

2614 

2615 Notes 

2616 ----- 

2617 For :math:`c=0`, `genextreme` is equal to `gumbel_r`. 

2618 The probability density function for `genextreme` is: 

2619 

2620 .. math:: 

2621 

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} 

2627 

2628 

2629 Note that several sources and software packages use the opposite 

2630 convention for the sign of the shape parameter :math:`c`. 

2631 

2632 `genextreme` takes ``c`` as a shape parameter for :math:`c`. 

2633 

2634 %(after_notes)s 

2635 

2636 %(example)s 

2637 

2638 """ 

2639 def _argcheck(self, c): 

2640 return np.where(abs(c) == np.inf, 0, 1) 

2641 

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 

2646 

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) 

2650 

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)) 

2656 

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 

2669 

2670 def _logcdf(self, x, c): 

2671 return -np.exp(self._loglogcdf(x, c)) 

2672 

2673 def _cdf(self, x, c): 

2674 return np.exp(self._logcdf(x, c)) 

2675 

2676 def _sf(self, x, c): 

2677 return -sc.expm1(self._logcdf(x, c)) 

2678 

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) 

2683 

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) 

2688 

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) 

2700 

2701 m = np.where(c < -1.0, np.nan, -gamk) 

2702 v = np.where(c < -0.5, np.nan, g1**2.0*gam2k) 

2703 

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) 

2711 

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 

2720 

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,)) 

2729 

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) 

2736 

2737 def _entropy(self, c): 

2738 return _EULER*(1 - c) + 1 

2739 

2740 

2741genextreme = genextreme_gen(name='genextreme') 

2742 

2743 

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) 

2771 

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) 

2776 

2777 return value[0] 

2778 

2779 

2780## Gamma (Use MATLAB and MATHEMATICA (b=theta=scale, a=alpha=shape) definition) 

2781 

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. 

2785 

2786class gamma_gen(rv_continuous): 

2787 r"""A gamma continuous random variable. 

2788 

2789 %(before_notes)s 

2790 

2791 See Also 

2792 -------- 

2793 erlang, expon 

2794 

2795 Notes 

2796 ----- 

2797 The probability density function for `gamma` is: 

2798 

2799 .. math:: 

2800 

2801 f(x, a) = \frac{x^{a-1} \exp(-x)}{\Gamma(a)} 

2802 

2803 for :math:`x \ge 0`, :math:`a > 0`. Here :math:`\Gamma(a)` refers to the 

2804 gamma function. 

2805 

2806 `gamma` takes ``a`` as a shape parameter for :math:`a`. 

2807 

2808 When :math:`a` is an integer, `gamma` reduces to the Erlang 

2809 distribution, and when :math:`a=1` to the exponential distribution. 

2810 

2811 %(after_notes)s 

2812 

2813 %(example)s 

2814 

2815 """ 

2816 def _rvs(self, a, size=None, random_state=None): 

2817 return random_state.standard_gamma(a, size) 

2818 

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)) 

2822 

2823 def _logpdf(self, x, a): 

2824 return sc.xlogy(a-1.0, x) - x - sc.gammaln(a) 

2825 

2826 def _cdf(self, x, a): 

2827 return sc.gammainc(a, x) 

2828 

2829 def _sf(self, x, a): 

2830 return sc.gammaincc(a, x) 

2831 

2832 def _ppf(self, q, a): 

2833 return sc.gammaincinv(a, q) 

2834 

2835 def _stats(self, a): 

2836 return a, a, 2.0/np.sqrt(a), 6.0/a 

2837 

2838 def _entropy(self, a): 

2839 return sc.psi(a)*(1-a) + a + sc.gammaln(a) 

2840 

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,)) 

2849 

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) 

2857 

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) 

2861 

2862 # We already have this value, so just pop it from kwds. 

2863 kwds.pop('floc', None) 

2864 

2865 f0 = _get_fixed_fit_value(kwds, ['f0', 'fa', 'fix_a']) 

2866 fscale = kwds.pop('fscale', None) 

2867 

2868 _remove_optimizer_parameters(kwds) 

2869 

2870 # Special case: loc is fixed. 

2871 

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.") 

2878 

2879 # Fixed location is handled by shifting the data. 

2880 data = np.asarray(data) 

2881 

2882 if not np.isfinite(data).all(): 

2883 raise RuntimeError("The data contains non-finite values.") 

2884 

2885 if np.any(data <= floc): 

2886 raise FitDataError("gamma", lower=floc, upper=np.inf) 

2887 

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() 

2893 

2894 # Three cases to handle: 

2895 # * shape and scale both free 

2896 # * shape fixed, scale free 

2897 # * shape free, scale fixed 

2898 

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) 

2915 

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 

2926 

2927 return a, floc, scale 

2928 

2929 

2930gamma = gamma_gen(a=0.0, name='gamma') 

2931 

2932 

2933class erlang_gen(gamma_gen): 

2934 """An Erlang continuous random variable. 

2935 

2936 %(before_notes)s 

2937 

2938 See Also 

2939 -------- 

2940 gamma 

2941 

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. 

2948 

2949 Refer to `gamma` for examples. 

2950 

2951 """ 

2952 

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 

2963 

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,)) 

2970 

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) 

2975 

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 """) 

2988 

2989 

2990erlang = erlang_gen(a=0.0, name='erlang') 

2991 

2992 

2993class gengamma_gen(rv_continuous): 

2994 r"""A generalized gamma continuous random variable. 

2995 

2996 %(before_notes)s 

2997 

2998 Notes 

2999 ----- 

3000 The probability density function for `gengamma` is: 

3001 

3002 .. math:: 

3003 

3004 f(x, a, c) = \frac{|c| x^{c a-1} \exp(-x^c)}{\Gamma(a)} 

3005 

3006 for :math:`x \ge 0`, :math:`a > 0`, and :math:`c \ne 0`. 

3007 :math:`\Gamma` is the gamma function (`scipy.special.gamma`). 

3008 

3009 `gengamma` takes :math:`a` and :math:`c` as shape parameters. 

3010 

3011 %(after_notes)s 

3012 

3013 %(example)s 

3014 

3015 """ 

3016 def _argcheck(self, a, c): 

3017 return (a > 0) & (c != 0) 

3018 

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)) 

3022 

3023 def _logpdf(self, x, a, c): 

3024 return np.log(abs(c)) + sc.xlogy(c*a - 1, x) - x**c - sc.gammaln(a) 

3025 

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) 

3031 

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) 

3037 

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) 

3042 

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) 

3047 

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) 

3051 

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)) 

3055 

3056 

3057gengamma = gengamma_gen(a=0.0, name='gengamma') 

3058 

3059 

3060class genhalflogistic_gen(rv_continuous): 

3061 r"""A generalized half-logistic continuous random variable. 

3062 

3063 %(before_notes)s 

3064 

3065 Notes 

3066 ----- 

3067 The probability density function for `genhalflogistic` is: 

3068 

3069 .. math:: 

3070 

3071 f(x, c) = \frac{2 (1 - c x)^{1/(c-1)}}{[1 + (1 - c x)^{1/c}]^2} 

3072 

3073 for :math:`0 \le x \le 1/c`, and :math:`c > 0`. 

3074 

3075 `genhalflogistic` takes ``c`` as a shape parameter for :math:`c`. 

3076 

3077 %(after_notes)s 

3078 

3079 %(example)s 

3080 

3081 """ 

3082 def _argcheck(self, c): 

3083 return c > 0 

3084 

3085 def _get_support(self, c): 

3086 return self.a, 1.0/c 

3087 

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 

3096 

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) 

3102 

3103 def _ppf(self, q, c): 

3104 return 1.0/c*(1-((1.0-q)/(1.0+q))**c) 

3105 

3106 def _entropy(self, c): 

3107 return 2 - (2*c+1)*np.log(2) 

3108 

3109 

3110genhalflogistic = genhalflogistic_gen(a=0.0, name='genhalflogistic') 

3111 

3112 

3113class gompertz_gen(rv_continuous): 

3114 r"""A Gompertz (or truncated Gumbel) continuous random variable. 

3115 

3116 %(before_notes)s 

3117 

3118 Notes 

3119 ----- 

3120 The probability density function for `gompertz` is: 

3121 

3122 .. math:: 

3123 

3124 f(x, c) = c \exp(x) \exp(-c (e^x-1)) 

3125 

3126 for :math:`x \ge 0`, :math:`c > 0`. 

3127 

3128 `gompertz` takes ``c`` as a shape parameter for :math:`c`. 

3129 

3130 %(after_notes)s 

3131 

3132 %(example)s 

3133 

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)) 

3138 

3139 def _logpdf(self, x, c): 

3140 return np.log(c) + x - c * sc.expm1(x) 

3141 

3142 def _cdf(self, x, c): 

3143 return -sc.expm1(-c * sc.expm1(x)) 

3144 

3145 def _ppf(self, q, c): 

3146 return sc.log1p(-1.0 / c * sc.log1p(-q)) 

3147 

3148 def _entropy(self, c): 

3149 return 1.0 - np.log(c) - np.exp(c)*sc.expn(1, c) 

3150 

3151 

3152gompertz = gompertz_gen(a=0.0, name='gompertz') 

3153 

3154 

3155class gumbel_r_gen(rv_continuous): 

3156 r"""A right-skewed Gumbel continuous random variable. 

3157 

3158 %(before_notes)s 

3159 

3160 See Also 

3161 -------- 

3162 gumbel_l, gompertz, genextreme 

3163 

3164 Notes 

3165 ----- 

3166 The probability density function for `gumbel_r` is: 

3167 

3168 .. math:: 

3169 

3170 f(x) = \exp(-(x + e^{-x})) 

3171 

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. 

3175 

3176 %(after_notes)s 

3177 

3178 %(example)s 

3179 

3180 """ 

3181 def _pdf(self, x): 

3182 # gumbel_r.pdf(x) = exp(-(x + exp(-x))) 

3183 return np.exp(self._logpdf(x)) 

3184 

3185 def _logpdf(self, x): 

3186 return -x - np.exp(-x) 

3187 

3188 def _cdf(self, x): 

3189 return np.exp(-np.exp(-x)) 

3190 

3191 def _logcdf(self, x): 

3192 return -np.exp(-x) 

3193 

3194 def _ppf(self, q): 

3195 return -np.log(-np.log(q)) 

3196 

3197 def _stats(self): 

3198 return _EULER, np.pi*np.pi/6.0, 12*np.sqrt(6)/np.pi**3 * _ZETA3, 12.0/5 

3199 

3200 def _entropy(self): 

3201 # https://en.wikipedia.org/wiki/Gumbel_distribution 

3202 return _EULER + 1. 

3203 

3204 

3205gumbel_r = gumbel_r_gen(name='gumbel_r') 

3206 

3207 

3208class gumbel_l_gen(rv_continuous): 

3209 r"""A left-skewed Gumbel continuous random variable. 

3210 

3211 %(before_notes)s 

3212 

3213 See Also 

3214 -------- 

3215 gumbel_r, gompertz, genextreme 

3216 

3217 Notes 

3218 ----- 

3219 The probability density function for `gumbel_l` is: 

3220 

3221 .. math:: 

3222 

3223 f(x) = \exp(x - e^x) 

3224 

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. 

3228 

3229 %(after_notes)s 

3230 

3231 %(example)s 

3232 

3233 """ 

3234 def _pdf(self, x): 

3235 # gumbel_l.pdf(x) = exp(x - exp(x)) 

3236 return np.exp(self._logpdf(x)) 

3237 

3238 def _logpdf(self, x): 

3239 return x - np.exp(x) 

3240 

3241 def _cdf(self, x): 

3242 return -sc.expm1(-np.exp(x)) 

3243 

3244 def _ppf(self, q): 

3245 return np.log(-sc.log1p(-q)) 

3246 

3247 def _logsf(self, x): 

3248 return -np.exp(x) 

3249 

3250 def _sf(self, x): 

3251 return np.exp(-np.exp(x)) 

3252 

3253 def _isf(self, x): 

3254 return np.log(-np.log(x)) 

3255 

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 

3259 

3260 def _entropy(self): 

3261 return _EULER + 1. 

3262 

3263 

3264gumbel_l = gumbel_l_gen(name='gumbel_l') 

3265 

3266 

3267class halfcauchy_gen(rv_continuous): 

3268 r"""A Half-Cauchy continuous random variable. 

3269 

3270 %(before_notes)s 

3271 

3272 Notes 

3273 ----- 

3274 The probability density function for `halfcauchy` is: 

3275 

3276 .. math:: 

3277 

3278 f(x) = \frac{2}{\pi (1 + x^2)} 

3279 

3280 for :math:`x \ge 0`. 

3281 

3282 %(after_notes)s 

3283 

3284 %(example)s 

3285 

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) 

3290 

3291 def _logpdf(self, x): 

3292 return np.log(2.0/np.pi) - sc.log1p(x*x) 

3293 

3294 def _cdf(self, x): 

3295 return 2.0/np.pi*np.arctan(x) 

3296 

3297 def _ppf(self, q): 

3298 return np.tan(np.pi/2*q) 

3299 

3300 def _stats(self): 

3301 return np.inf, np.inf, np.nan, np.nan 

3302 

3303 def _entropy(self): 

3304 return np.log(2*np.pi) 

3305 

3306 

3307halfcauchy = halfcauchy_gen(a=0.0, name='halfcauchy') 

3308 

3309 

3310class halflogistic_gen(rv_continuous): 

3311 r"""A half-logistic continuous random variable. 

3312 

3313 %(before_notes)s 

3314 

3315 Notes 

3316 ----- 

3317 The probability density function for `halflogistic` is: 

3318 

3319 .. math:: 

3320 

3321 f(x) = \frac{ 2 e^{-x} }{ (1+e^{-x})^2 } 

3322 = \frac{1}{2} \text{sech}(x/2)^2 

3323 

3324 for :math:`x \ge 0`. 

3325 

3326 %(after_notes)s 

3327 

3328 %(example)s 

3329 

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)) 

3335 

3336 def _logpdf(self, x): 

3337 return np.log(2) - x - 2. * sc.log1p(np.exp(-x)) 

3338 

3339 def _cdf(self, x): 

3340 return np.tanh(x/2.0) 

3341 

3342 def _ppf(self, q): 

3343 return 2*np.arctanh(q) 

3344 

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) 

3355 

3356 def _entropy(self): 

3357 return 2-np.log(2) 

3358 

3359 

3360halflogistic = halflogistic_gen(a=0.0, name='halflogistic') 

3361 

3362 

3363class halfnorm_gen(rv_continuous): 

3364 r"""A half-normal continuous random variable. 

3365 

3366 %(before_notes)s 

3367 

3368 Notes 

3369 ----- 

3370 The probability density function for `halfnorm` is: 

3371 

3372 .. math:: 

3373 

3374 f(x) = \sqrt{2/\pi} \exp(-x^2 / 2) 

3375 

3376 for :math:`x >= 0`. 

3377 

3378 `halfnorm` is a special case of `chi` with ``df=1``. 

3379 

3380 %(after_notes)s 

3381 

3382 %(example)s 

3383 

3384 """ 

3385 def _rvs(self, size=None, random_state=None): 

3386 return abs(random_state.standard_normal(size=size)) 

3387 

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) 

3391 

3392 def _logpdf(self, x): 

3393 return 0.5 * np.log(2.0/np.pi) - x*x/2.0 

3394 

3395 def _cdf(self, x): 

3396 return _norm_cdf(x)*2-1.0 

3397 

3398 def _ppf(self, q): 

3399 return sc.ndtri((1+q)/2.0) 

3400 

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) 

3406 

3407 def _entropy(self): 

3408 return 0.5*np.log(np.pi/2.0)+0.5 

3409 

3410 

3411halfnorm = halfnorm_gen(a=0.0, name='halfnorm') 

3412 

3413 

3414class hypsecant_gen(rv_continuous): 

3415 r"""A hyperbolic secant continuous random variable. 

3416 

3417 %(before_notes)s 

3418 

3419 Notes 

3420 ----- 

3421 The probability density function for `hypsecant` is: 

3422 

3423 .. math:: 

3424 

3425 f(x) = \frac{1}{\pi} \text{sech}(x) 

3426 

3427 for a real number :math:`x`. 

3428 

3429 %(after_notes)s 

3430 

3431 %(example)s 

3432 

3433 """ 

3434 def _pdf(self, x): 

3435 # hypsecant.pdf(x) = 1/pi * sech(x) 

3436 return 1.0/(np.pi*np.cosh(x)) 

3437 

3438 def _cdf(self, x): 

3439 return 2.0/np.pi*np.arctan(np.exp(x)) 

3440 

3441 def _ppf(self, q): 

3442 return np.log(np.tan(np.pi*q/2.0)) 

3443 

3444 def _stats(self): 

3445 return 0, np.pi*np.pi/4, 0, 2 

3446 

3447 def _entropy(self): 

3448 return np.log(2*np.pi) 

3449 

3450 

3451hypsecant = hypsecant_gen(name='hypsecant') 

3452 

3453 

3454class gausshyper_gen(rv_continuous): 

3455 r"""A Gauss hypergeometric continuous random variable. 

3456 

3457 %(before_notes)s 

3458 

3459 Notes 

3460 ----- 

3461 The probability density function for `gausshyper` is: 

3462 

3463 .. math:: 

3464 

3465 f(x, a, b, c, z) = C x^{a-1} (1-x)^{b-1} (1+zx)^{-c} 

3466 

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`. 

3471 

3472 `gausshyper` takes :math:`a`, :math:`b`, :math:`c` and :math:`z` as shape 

3473 parameters. 

3474 

3475 %(after_notes)s 

3476 

3477 %(example)s 

3478 

3479 """ 

3480 def _argcheck(self, a, b, c, z): 

3481 return (a > 0) & (b > 0) & (c == c) & (z == z) 

3482 

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 

3488 

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 

3494 

3495 

3496gausshyper = gausshyper_gen(a=0.0, b=1.0, name='gausshyper') 

3497 

3498 

3499class invgamma_gen(rv_continuous): 

3500 r"""An inverted gamma continuous random variable. 

3501 

3502 %(before_notes)s 

3503 

3504 Notes 

3505 ----- 

3506 The probability density function for `invgamma` is: 

3507 

3508 .. math:: 

3509 

3510 f(x, a) = \frac{x^{-a-1}}{\Gamma(a)} \exp(-\frac{1}{x}) 

3511 

3512 for :math:`x >= 0`, :math:`a > 0`. :math:`\Gamma` is the gamma function 

3513 (`scipy.special.gamma`). 

3514 

3515 `invgamma` takes ``a`` as a shape parameter for :math:`a`. 

3516 

3517 `invgamma` is a special case of `gengamma` with ``c=-1``. 

3518 

3519 %(after_notes)s 

3520 

3521 %(example)s 

3522 

3523 """ 

3524 _support_mask = rv_continuous._open_support_mask 

3525 

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)) 

3529 

3530 def _logpdf(self, x, a): 

3531 return -(a+1) * np.log(x) - sc.gammaln(a) - 1.0/x 

3532 

3533 def _cdf(self, x, a): 

3534 return sc.gammaincc(a, 1.0 / x) 

3535 

3536 def _ppf(self, q, a): 

3537 return 1.0 / sc.gammainccinv(a, q) 

3538 

3539 def _sf(self, x, a): 

3540 return sc.gammainc(a, 1.0 / x) 

3541 

3542 def _isf(self, q, a): 

3543 return 1.0 / sc.gammaincinv(a, q) 

3544 

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) 

3549 

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 

3560 

3561 def _entropy(self, a): 

3562 return a - (a+1.0) * sc.psi(a) + sc.gammaln(a) 

3563 

3564 

3565invgamma = invgamma_gen(a=0.0, name='invgamma') 

3566 

3567 

3568# scale is gamma from DATAPLOT and B from Regress 

3569class invgauss_gen(rv_continuous): 

3570 r"""An inverse Gaussian continuous random variable. 

3571 

3572 %(before_notes)s 

3573 

3574 Notes 

3575 ----- 

3576 The probability density function for `invgauss` is: 

3577 

3578 .. math:: 

3579 

3580 f(x, \mu) = \frac{1}{\sqrt{2 \pi x^3}} 

3581 \exp(-\frac{(x-\mu)^2}{2 x \mu^2}) 

3582 

3583 for :math:`x >= 0` and :math:`\mu > 0`. 

3584 

3585 `invgauss` takes ``mu`` as a shape parameter for :math:`\mu`. 

3586 

3587 %(after_notes)s 

3588 

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`. 

3592 

3593 %(example)s 

3594 

3595 """ 

3596 _support_mask = rv_continuous._open_support_mask 

3597 

3598 def _rvs(self, mu, size=None, random_state=None): 

3599 return random_state.wald(mu, 1.0, size=size) 

3600 

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) 

3605 

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) 

3608 

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 

3615 

3616 def _stats(self, mu): 

3617 return mu, mu**3.0, 3*np.sqrt(mu), 15*mu 

3618 

3619 

3620invgauss = invgauss_gen(a=0.0, name='invgauss') 

3621 

3622 

3623class geninvgauss_gen(rv_continuous): 

3624 r"""A Generalized Inverse Gaussian continuous random variable. 

3625 

3626 %(before_notes)s 

3627 

3628 Notes 

3629 ----- 

3630 The probability density function for `geninvgauss` is: 

3631 

3632 .. math:: 

3633 

3634 f(x, p, b) = x^{p-1} \exp(-b (x + 1/x) / 2) / (2 K_p(b)) 

3635 

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`). 

3639 

3640 %(after_notes)s 

3641 

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`. 

3644 

3645 Generating random variates is challenging for this distribution. The 

3646 implementation is based on [2]_. 

3647 

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. 

3653 

3654 .. [2] W. Hoermann and J. Leydold, "Generating generalized inverse Gaussian 

3655 random variates", Statistics and Computing, 24(4), p. 547--557, 2014. 

3656 

3657 %(example)s 

3658 

3659 """ 

3660 def _argcheck(self, p, b): 

3661 return (p == p) & (b > 0) 

3662 

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) 

3670 

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 

3677 

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)) 

3681 

3682 def _cdf(self, x, *args): 

3683 _a, _b = self._get_support(*args) 

3684 

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) 

3690 

3691 return integrate.quad(llc, _a, x)[0] 

3692 

3693 return _cdf_single(x, *args) 

3694 

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) 

3700 

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 (). 

3712 

3713 p, b = np.broadcast_arrays(p, b) 

3714 # p and b now have the same shape. 

3715 

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) 

3724 

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)) 

3728 

3729 # `out` is the array to be returned. It is filled in in the 

3730 # loop below. 

3731 out = np.empty(size) 

3732 

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() 

3750 

3751 if size == (): 

3752 out = out[()] 

3753 return out 

3754 

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) 

3766 

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 

3778 

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 

3784 

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 

3798 

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 

3811 

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) 

3824 

3825 if vmin >= vmax: 

3826 raise ValueError("vmin must be smaller than vmax.") 

3827 if umax <= 0: 

3828 raise ValueError("umax must be positive.") 

3829 

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 

3844 

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 

3868 

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 

3898 

3899 rvs = np.reshape(x, size1d) 

3900 if invert_res: 

3901 rvs = 1 / rvs 

3902 return rvs 

3903 

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 

3910 

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 

3925 

3926 

3927geninvgauss = geninvgauss_gen(a=0.0, name="geninvgauss") 

3928 

3929 

3930class norminvgauss_gen(rv_continuous): 

3931 r"""A Normal Inverse Gaussian continuous random variable. 

3932 

3933 %(before_notes)s 

3934 

3935 Notes 

3936 ----- 

3937 The probability density function for `norminvgauss` is: 

3938 

3939 .. math:: 

3940 

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) 

3943 

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`). 

3949 

3950 %(after_notes)s 

3951 

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. 

3957 

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. 

3963 

3964 O. Barndorff-Nielsen, "Normal Inverse Gaussian Distributions and Stochastic 

3965 Volatility Modelling", Scandinavian Journal of Statistics, Vol. 24, 

3966 pp. 1-13, 1997. 

3967 

3968 %(example)s 

3969 

3970 """ 

3971 _support_mask = rv_continuous._open_support_mask 

3972 

3973 def _argcheck(self, a, b): 

3974 return (a > 0) & (np.absolute(b) < a) 

3975 

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 

3981 

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) 

3988 

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 

3996 

3997 

3998norminvgauss = norminvgauss_gen(name="norminvgauss") 

3999 

4000 

4001class invweibull_gen(rv_continuous): 

4002 u"""An inverted Weibull continuous random variable. 

4003 

4004 This distribution is also known as the Fréchet distribution or the 

4005 type II extreme value distribution. 

4006 

4007 %(before_notes)s 

4008 

4009 Notes 

4010 ----- 

4011 The probability density function for `invweibull` is: 

4012 

4013 .. math:: 

4014 

4015 f(x, c) = c x^{-c-1} \\exp(-x^{-c}) 

4016 

4017 for :math:`x > 0`, :math:`c > 0`. 

4018 

4019 `invweibull` takes ``c`` as a shape parameter for :math:`c`. 

4020 

4021 %(after_notes)s 

4022 

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. 

4027 

4028 %(example)s 

4029 

4030 """ 

4031 _support_mask = rv_continuous._open_support_mask 

4032 

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 

4039 

4040 def _cdf(self, x, c): 

4041 xc1 = np.power(x, -c) 

4042 return np.exp(-xc1) 

4043 

4044 def _ppf(self, q, c): 

4045 return np.power(-np.log(q), -1.0/c) 

4046 

4047 def _munp(self, n, c): 

4048 return sc.gamma(1 - n / c) 

4049 

4050 def _entropy(self, c): 

4051 return 1+_EULER + _EULER / c - np.log(c) 

4052 

4053 

4054invweibull = invweibull_gen(a=0, name='invweibull') 

4055 

4056 

4057class johnsonsb_gen(rv_continuous): 

4058 r"""A Johnson SB continuous random variable. 

4059 

4060 %(before_notes)s 

4061 

4062 See Also 

4063 -------- 

4064 johnsonsu 

4065 

4066 Notes 

4067 ----- 

4068 The probability density function for `johnsonsb` is: 

4069 

4070 .. math:: 

4071 

4072 f(x, a, b) = \frac{b}{x(1-x)} \phi(a + b \log \frac{x}{1-x} ) 

4073 

4074 for :math:`0 <= x < =1` and :math:`a, b > 0`, and :math:`\phi` is the normal 

4075 pdf. 

4076 

4077 `johnsonsb` takes :math:`a` and :math:`b` as shape parameters. 

4078 

4079 %(after_notes)s 

4080 

4081 %(example)s 

4082 

4083 """ 

4084 _support_mask = rv_continuous._open_support_mask 

4085 

4086 def _argcheck(self, a, b): 

4087 return (b > 0) & (a == a) 

4088 

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 

4093 

4094 def _cdf(self, x, a, b): 

4095 return _norm_cdf(a + b*np.log(x/(1.0-x))) 

4096 

4097 def _ppf(self, q, a, b): 

4098 return 1.0 / (1 + np.exp(-1.0 / b * (_norm_ppf(q) - a))) 

4099 

4100 

4101johnsonsb = johnsonsb_gen(a=0.0, b=1.0, name='johnsonsb') 

4102 

4103 

4104class johnsonsu_gen(rv_continuous): 

4105 r"""A Johnson SU continuous random variable. 

4106 

4107 %(before_notes)s 

4108 

4109 See Also 

4110 -------- 

4111 johnsonsb 

4112 

4113 Notes 

4114 ----- 

4115 The probability density function for `johnsonsu` is: 

4116 

4117 .. math:: 

4118 

4119 f(x, a, b) = \frac{b}{\sqrt{x^2 + 1}} 

4120 \phi(a + b \log(x + \sqrt{x^2 + 1})) 

4121 

4122 for all :math:`x, a, b > 0`, and :math:`\phi` is the normal pdf. 

4123 

4124 `johnsonsu` takes :math:`a` and :math:`b` as shape parameters. 

4125 

4126 %(after_notes)s 

4127 

4128 %(example)s 

4129 

4130 """ 

4131 def _argcheck(self, a, b): 

4132 return (b > 0) & (a == a) 

4133 

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 

4140 

4141 def _cdf(self, x, a, b): 

4142 return _norm_cdf(a + b * np.log(x + np.sqrt(x*x + 1))) 

4143 

4144 def _ppf(self, q, a, b): 

4145 return np.sinh((_norm_ppf(q) - a) / b) 

4146 

4147 

4148johnsonsu = johnsonsu_gen(name='johnsonsu') 

4149 

4150 

4151class laplace_gen(rv_continuous): 

4152 r"""A Laplace continuous random variable. 

4153 

4154 %(before_notes)s 

4155 

4156 Notes 

4157 ----- 

4158 The probability density function for `laplace` is 

4159 

4160 .. math:: 

4161 

4162 f(x) = \frac{1}{2} \exp(-|x|) 

4163 

4164 for a real number :math:`x`. 

4165 

4166 %(after_notes)s 

4167 

4168 %(example)s 

4169 

4170 """ 

4171 def _rvs(self, size=None, random_state=None): 

4172 return random_state.laplace(0, 1, size=size) 

4173 

4174 def _pdf(self, x): 

4175 # laplace.pdf(x) = 1/2 * exp(-abs(x)) 

4176 return 0.5*np.exp(-abs(x)) 

4177 

4178 def _cdf(self, x): 

4179 return np.where(x > 0, 1.0-0.5*np.exp(-x), 0.5*np.exp(x)) 

4180 

4181 def _ppf(self, q): 

4182 return np.where(q > 0.5, -np.log(2*(1-q)), np.log(2*q)) 

4183 

4184 def _stats(self): 

4185 return 0, 2, 0, 3 

4186 

4187 def _entropy(self): 

4188 return np.log(2)+1 

4189 

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) 

4197 

4198 _check_fit_input_parameters(data, args, 

4199 kwds, fixed_param=(floc, fscale)) 

4200 

4201 # MLE for the laplace distribution 

4202 

4203 if floc is None: 

4204 loc = np.median(data) 

4205 else: 

4206 loc = floc 

4207 

4208 if fscale is None: 

4209 scale = (np.sum(np.abs(data - loc))) / len(data) 

4210 else: 

4211 scale = fscale 

4212 

4213 # Source: Statistical Distributions, 3rd Edition. Evans, Hastings, 

4214 # and Peacock (2000), Page 124 

4215 

4216 return loc, scale 

4217 

4218 

4219laplace = laplace_gen(name='laplace') 

4220 

4221 

4222def _check_fit_input_parameters(data, args, kwds, fixed_param): 

4223 if len(args) > 0: 

4224 raise TypeError("Too many arguments.") 

4225 

4226 _remove_optimizer_parameters(kwds) 

4227 

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.") 

4234 

4235 data = np.asarray(data) 

4236 if not np.isfinite(data).all(): 

4237 raise RuntimeError("The data contains non-finite values.") 

4238 

4239 

4240class levy_gen(rv_continuous): 

4241 r"""A Levy continuous random variable. 

4242 

4243 %(before_notes)s 

4244 

4245 See Also 

4246 -------- 

4247 levy_stable, levy_l 

4248 

4249 Notes 

4250 ----- 

4251 The probability density function for `levy` is: 

4252 

4253 .. math:: 

4254 

4255 f(x) = \frac{1}{\sqrt{2\pi x^3}} \exp\left(-\frac{1}{2x}\right) 

4256 

4257 for :math:`x >= 0`. 

4258 

4259 This is the same as the Levy-stable distribution with :math:`a=1/2` and 

4260 :math:`b=1`. 

4261 

4262 %(after_notes)s 

4263 

4264 %(example)s 

4265 

4266 """ 

4267 _support_mask = rv_continuous._open_support_mask 

4268 

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)) 

4272 

4273 def _cdf(self, x): 

4274 # Equivalent to 2*norm.sf(np.sqrt(1/x)) 

4275 return sc.erfc(np.sqrt(0.5 / x)) 

4276 

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) 

4281 

4282 def _stats(self): 

4283 return np.inf, np.inf, np.nan, np.nan 

4284 

4285 

4286levy = levy_gen(a=0.0, name="levy") 

4287 

4288 

4289class levy_l_gen(rv_continuous): 

4290 r"""A left-skewed Levy continuous random variable. 

4291 

4292 %(before_notes)s 

4293 

4294 See Also 

4295 -------- 

4296 levy, levy_stable 

4297 

4298 Notes 

4299 ----- 

4300 The probability density function for `levy_l` is: 

4301 

4302 .. math:: 

4303 f(x) = \frac{1}{|x| \sqrt{2\pi |x|}} \exp{ \left(-\frac{1}{2|x|} \right)} 

4304 

4305 for :math:`x <= 0`. 

4306 

4307 This is the same as the Levy-stable distribution with :math:`a=1/2` and 

4308 :math:`b=-1`. 

4309 

4310 %(after_notes)s 

4311 

4312 %(example)s 

4313 

4314 """ 

4315 _support_mask = rv_continuous._open_support_mask 

4316 

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)) 

4321 

4322 def _cdf(self, x): 

4323 ax = abs(x) 

4324 return 2 * _norm_cdf(1 / np.sqrt(ax)) - 1 

4325 

4326 def _ppf(self, q): 

4327 val = _norm_ppf((q + 1.0) / 2) 

4328 return -1.0 / (val * val) 

4329 

4330 def _stats(self): 

4331 return np.inf, np.inf, np.nan, np.nan 

4332 

4333 

4334levy_l = levy_l_gen(b=0.0, name="levy_l") 

4335 

4336 

4337class levy_stable_gen(rv_continuous): 

4338 r"""A Levy-stable continuous random variable. 

4339 

4340 %(before_notes)s 

4341 

4342 See Also 

4343 -------- 

4344 levy, levy_l 

4345 

4346 Notes 

4347 ----- 

4348 The distribution for `levy_stable` has characteristic function: 

4349 

4350 .. math:: 

4351 

4352 \varphi(t, \alpha, \beta, c, \mu) = 

4353 e^{it\mu -|ct|^{\alpha}(1-i\beta \operatorname{sign}(t)\Phi(\alpha, t))} 

4354 

4355 where: 

4356 

4357 .. math:: 

4358 

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} 

4363 

4364 The probability density function for `levy_stable` is: 

4365 

4366 .. math:: 

4367 

4368 f(x) = \frac{1}{2\pi}\int_{-\infty}^\infty \varphi(t)e^{-ixt}\,dt 

4369 

4370 where :math:`-\infty < t < \infty`. This integral does not have a known closed form. 

4371 

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. 

4377 

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'. 

4381 

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. 

4386 

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. 

4391 

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. 

4395 

4396 .. warning:: 

4397 

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. 

4401 

4402 For cdf calculations FFT calculation is considered experimental. Use Zolatarev's method 

4403 instead (default). 

4404 

4405 %(after_notes)s 

4406 

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. 

4415 

4416 %(example)s 

4417 

4418 """ 

4419 

4420 def _rvs(self, alpha, beta, size=None, random_state=None): 

4421 

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))) 

4425 

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)) 

4429 

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 

4438 

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 

4444 

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 

4458 

4459 def _argcheck(self, alpha, beta): 

4460 return (alpha > 0) & (alpha <= 2) & (beta <= 1) & (beta >= -1) 

4461 

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))) 

4466 

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) 

4477 

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) 

4484 

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 

4489 

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 

4498 

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))) 

4506 

4507 def f(theta): 

4508 return g(theta) * np.exp(-g(theta)) 

4509 

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. 

4514 

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) 

4534 

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 

4538 

4539 def g(theta): 

4540 return np.exp(-np.pi * x / 2. / beta) * V(theta) 

4541 

4542 def f(theta): 

4543 return g(theta) * np.exp(-g(theta)) 

4544 

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 

4551 

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 

4560 

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 

4567 

4568 def f(theta): 

4569 return np.exp(-V(theta)*np.real(np.complex(x0-zeta)**(alpha/(alpha-1)))) 

4570 

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) 

4583 

4584 else: 

4585 # since location zero, no need to reposition x for S_0 parameterization 

4586 xi = np.pi/2 

4587 if beta > 0: 

4588 

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 

4592 

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) 

4601 

4602 def _pdf(self, x, alpha, beta): 

4603 

4604 x = np.asarray(x).reshape(1, -1)[0,:] 

4605 

4606 x, alpha, beta = np.broadcast_arrays(x, alpha, beta) 

4607 

4608 data_in = np.dstack((x, alpha, beta))[0] 

4609 data_out = np.empty(shape=(len(data_in),1)) 

4610 

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 

4618 

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) 

4622 

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,)] 

4637 

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) 

4641 

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) 

4645 

4646 return data_out.T[0] 

4647 

4648 def _cdf(self, x, alpha, beta): 

4649 

4650 x = np.asarray(x).reshape(1, -1)[0,:] 

4651 

4652 x, alpha, beta = np.broadcast_arrays(x, alpha, beta) 

4653 

4654 data_in = np.dstack((x, alpha, beta))[0] 

4655 data_out = np.empty(shape=(len(data_in),1)) 

4656 

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) 

4660 

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,)] 

4677 

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) 

4681 

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) 

4685 

4686 return data_out.T[0] 

4687 

4688 def _fitstart(self, data): 

4689 # We follow McCullock 1986 method - Simple Consistent Estimators 

4690 # of Stable Distribution Parameters 

4691 

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] 

4695 

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]] 

4713 

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]] 

4731 

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] 

4735 

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]] 

4754 

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]] 

4773 

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) 

4777 

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) 

4782 

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) 

4789 

4790 nu_alpha = (p95 - p05)/(p75 - p25) 

4791 nu_beta = (p95 + p05 - 2*p50)/(p95 - p05) 

4792 

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) 

4802 

4803 return (alpha, beta, delta, c) 

4804 

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 

4811 

4812 

4813levy_stable = levy_stable_gen(name='levy_stable') 

4814 

4815 

4816class logistic_gen(rv_continuous): 

4817 r"""A logistic (or Sech-squared) continuous random variable. 

4818 

4819 %(before_notes)s 

4820 

4821 Notes 

4822 ----- 

4823 The probability density function for `logistic` is: 

4824 

4825 .. math:: 

4826 

4827 f(x) = \frac{\exp(-x)} 

4828 {(1+\exp(-x))^2} 

4829 

4830 `logistic` is a special case of `genlogistic` with ``c=1``. 

4831 

4832 %(after_notes)s 

4833 

4834 %(example)s 

4835 

4836 """ 

4837 def _rvs(self, size=None, random_state=None): 

4838 return random_state.logistic(size=size) 

4839 

4840 def _pdf(self, x): 

4841 # logistic.pdf(x) = exp(-x) / (1+exp(-x))**2 

4842 return np.exp(self._logpdf(x)) 

4843 

4844 def _logpdf(self, x): 

4845 return -x - 2. * sc.log1p(np.exp(-x)) 

4846 

4847 def _cdf(self, x): 

4848 return sc.expit(x) 

4849 

4850 def _ppf(self, q): 

4851 return sc.logit(q) 

4852 

4853 def _sf(self, x): 

4854 return sc.expit(-x) 

4855 

4856 def _isf(self, q): 

4857 return -sc.logit(q) 

4858 

4859 def _stats(self): 

4860 return 0, np.pi*np.pi/3.0, 0, 6.0/5.0 

4861 

4862 def _entropy(self): 

4863 # https://en.wikipedia.org/wiki/Logistic_distribution 

4864 return 2.0 

4865 

4866 

4867logistic = logistic_gen(name='logistic') 

4868 

4869 

4870class loggamma_gen(rv_continuous): 

4871 r"""A log gamma continuous random variable. 

4872 

4873 %(before_notes)s 

4874 

4875 Notes 

4876 ----- 

4877 The probability density function for `loggamma` is: 

4878 

4879 .. math:: 

4880 

4881 f(x, c) = \frac{\exp(c x - \exp(x))} 

4882 {\Gamma(c)} 

4883 

4884 for all :math:`x, c > 0`. Here, :math:`\Gamma` is the 

4885 gamma function (`scipy.special.gamma`). 

4886 

4887 `loggamma` takes ``c`` as a shape parameter for :math:`c`. 

4888 

4889 %(after_notes)s 

4890 

4891 %(example)s 

4892 

4893 """ 

4894 def _rvs(self, c, size=None, random_state=None): 

4895 return np.log(random_state.gamma(c, size=size)) 

4896 

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)) 

4900 

4901 def _cdf(self, x, c): 

4902 return sc.gammainc(c, np.exp(x)) 

4903 

4904 def _ppf(self, q, c): 

4905 return np.log(sc.gammaincinv(c, q)) 

4906 

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 

4915 

4916 

4917loggamma = loggamma_gen(name='loggamma') 

4918 

4919 

4920class loglaplace_gen(rv_continuous): 

4921 r"""A log-Laplace continuous random variable. 

4922 

4923 %(before_notes)s 

4924 

4925 Notes 

4926 ----- 

4927 The probability density function for `loglaplace` is: 

4928 

4929 .. math:: 

4930 

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} 

4934 

4935 for :math:`c > 0`. 

4936 

4937 `loglaplace` takes ``c`` as a shape parameter for :math:`c`. 

4938 

4939 %(after_notes)s 

4940 

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. 

4945 

4946 %(example)s 

4947 

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) 

4955 

4956 def _cdf(self, x, c): 

4957 return np.where(x < 1, 0.5*x**c, 1-0.5*x**(-c)) 

4958 

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)) 

4961 

4962 def _munp(self, n, c): 

4963 return c**2 / (c**2 - n**2) 

4964 

4965 def _entropy(self, c): 

4966 return np.log(2.0/c) + 1.0 

4967 

4968 

4969loglaplace = loglaplace_gen(a=0.0, name='loglaplace') 

4970 

4971 

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) 

4976 

4977 

4978class lognorm_gen(rv_continuous): 

4979 r"""A lognormal continuous random variable. 

4980 

4981 %(before_notes)s 

4982 

4983 Notes 

4984 ----- 

4985 The probability density function for `lognorm` is: 

4986 

4987 .. math:: 

4988 

4989 f(x, s) = \frac{1}{s x \sqrt{2\pi}} 

4990 \exp\left(-\frac{\log^2(x)}{2s^2}\right) 

4991 

4992 for :math:`x > 0`, :math:`s > 0`. 

4993 

4994 `lognorm` takes ``s`` as a shape parameter for :math:`s`. 

4995 

4996 %(after_notes)s 

4997 

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)``. 

5003 

5004 %(example)s 

5005 

5006 """ 

5007 _support_mask = rv_continuous._open_support_mask 

5008 

5009 def _rvs(self, s, size=None, random_state=None): 

5010 return np.exp(s * random_state.standard_normal(size)) 

5011 

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)) 

5015 

5016 def _logpdf(self, x, s): 

5017 return _lognorm_logpdf(x, s) 

5018 

5019 def _cdf(self, x, s): 

5020 return _norm_cdf(np.log(x) / s) 

5021 

5022 def _logcdf(self, x, s): 

5023 return _norm_logcdf(np.log(x) / s) 

5024 

5025 def _ppf(self, q, s): 

5026 return np.exp(s * _norm_ppf(q)) 

5027 

5028 def _sf(self, x, s): 

5029 return _norm_sf(np.log(x) / s) 

5030 

5031 def _logsf(self, x, s): 

5032 return _norm_logsf(np.log(x) / s) 

5033 

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 

5041 

5042 def _entropy(self, s): 

5043 return 0.5 * (1 + np.log(2*np.pi) + 2 * np.log(s)) 

5044 

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) 

5055 

5056 f0 = (kwds.get('f0', None) or kwds.get('fs', None) or 

5057 kwds.get('fix_s', None)) 

5058 fscale = kwds.get('fscale', None) 

5059 

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) 

5067 

5068 # Special case: loc is fixed. Use the maximum likelihood formulas 

5069 # instead of the numerical solver. 

5070 

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.") 

5075 

5076 data = np.asarray(data) 

5077 

5078 if not np.isfinite(data).all(): 

5079 raise RuntimeError("The data contains non-finite values.") 

5080 

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) 

5089 

5090 # Three cases to handle: 

5091 # * shape and scale both free 

5092 # * shape fixed, scale free 

5093 # * shape free, scale fixed 

5094 

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()) 

5108 

5109 return shape, floc, scale 

5110 

5111 

5112lognorm = lognorm_gen(a=0.0, name='lognorm') 

5113 

5114 

5115class gilbrat_gen(rv_continuous): 

5116 r"""A Gilbrat continuous random variable. 

5117 

5118 %(before_notes)s 

5119 

5120 Notes 

5121 ----- 

5122 The probability density function for `gilbrat` is: 

5123 

5124 .. math:: 

5125 

5126 f(x) = \frac{1}{x \sqrt{2\pi}} \exp(-\frac{1}{2} (\log(x))^2) 

5127 

5128 `gilbrat` is a special case of `lognorm` with ``s=1``. 

5129 

5130 %(after_notes)s 

5131 

5132 %(example)s 

5133 

5134 """ 

5135 _support_mask = rv_continuous._open_support_mask 

5136 

5137 def _rvs(self, size=None, random_state=None): 

5138 return np.exp(random_state.standard_normal(size)) 

5139 

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)) 

5143 

5144 def _logpdf(self, x): 

5145 return _lognorm_logpdf(x, 1.0) 

5146 

5147 def _cdf(self, x): 

5148 return _norm_cdf(np.log(x)) 

5149 

5150 def _ppf(self, q): 

5151 return np.exp(_norm_ppf(q)) 

5152 

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 

5160 

5161 def _entropy(self): 

5162 return 0.5 * np.log(2 * np.pi) + 0.5 

5163 

5164 

5165gilbrat = gilbrat_gen(a=0.0, name='gilbrat') 

5166 

5167 

5168class maxwell_gen(rv_continuous): 

5169 r"""A Maxwell continuous random variable. 

5170 

5171 %(before_notes)s 

5172 

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]_. 

5178 

5179 The probability density function for `maxwell` is: 

5180 

5181 .. math:: 

5182 

5183 f(x) = \sqrt{2/\pi}x^2 \exp(-x^2/2) 

5184 

5185 for :math:`x >= 0`. 

5186 

5187 %(after_notes)s 

5188 

5189 References 

5190 ---------- 

5191 .. [1] http://mathworld.wolfram.com/MaxwellDistribution.html 

5192 

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) 

5197 

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) 

5201 

5202 def _logpdf(self, x): 

5203 return _LOG_SQRT_2_OVER_PI + 2*np.log(x) - 0.5*x*x 

5204 

5205 def _cdf(self, x): 

5206 return sc.gammainc(1.5, x*x/2.0) 

5207 

5208 def _ppf(self, q): 

5209 return np.sqrt(2*sc.gammaincinv(1.5, q)) 

5210 

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) 

5217 

5218 def _entropy(self): 

5219 return _EULER + 0.5*np.log(2*np.pi)-0.5 

5220 

5221 

5222maxwell = maxwell_gen(a=0.0, name='maxwell') 

5223 

5224 

5225class mielke_gen(rv_continuous): 

5226 r"""A Mielke Beta-Kappa / Dagum continuous random variable. 

5227 

5228 %(before_notes)s 

5229 

5230 Notes 

5231 ----- 

5232 The probability density function for `mielke` is: 

5233 

5234 .. math:: 

5235 

5236 f(x, k, s) = \frac{k x^{k-1}}{(1+x^s)^{1+k/s}} 

5237 

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``). 

5242 

5243 `mielke` takes ``k`` and ``s`` as shape parameters. 

5244 

5245 %(after_notes)s 

5246 

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). 

5255 

5256 %(example)s 

5257 

5258 """ 

5259 def _argcheck(self, k, s): 

5260 return (k > 0) & (s > 0) 

5261 

5262 def _pdf(self, x, k, s): 

5263 return k*x**(k-1.0) / (1.0+x**s)**(1.0+k*1.0/s) 

5264 

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) 

5267 

5268 def _cdf(self, x, k, s): 

5269 return x**k / (1.0+x**s)**(k*1.0/s) 

5270 

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) 

5274 

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) 

5279 

5280 return _lazywhere(n < s, (n, k, s), nth_moment, np.inf) 

5281 

5282 

5283mielke = mielke_gen(a=0.0, name='mielke') 

5284 

5285 

5286class kappa4_gen(rv_continuous): 

5287 r"""Kappa 4 parameter distribution. 

5288 

5289 %(before_notes)s 

5290 

5291 Notes 

5292 ----- 

5293 The probability density function for kappa4 is: 

5294 

5295 .. math:: 

5296 

5297 f(x, h, k) = (1 - k x)^{1/k - 1} (1 - h (1 - k x)^{1/k})^{1/h-1} 

5298 

5299 if :math:`h` and :math:`k` are not equal to 0. 

5300 

5301 If :math:`h` or :math:`k` are zero then the pdf can be simplified: 

5302 

5303 h = 0 and k != 0:: 

5304 

5305 kappa4.pdf(x, h, k) = (1.0 - k*x)**(1.0/k - 1.0)* 

5306 exp(-(1.0 - k*x)**(1.0/k)) 

5307 

5308 h != 0 and k = 0:: 

5309 

5310 kappa4.pdf(x, h, k) = exp(-x)*(1.0 - h*exp(-x))**(1.0/h - 1.0) 

5311 

5312 h = 0 and k = 0:: 

5313 

5314 kappa4.pdf(x, h, k) = exp(-x)*exp(-exp(-x)) 

5315 

5316 kappa4 takes :math:`h` and :math:`k` as shape parameters. 

5317 

5318 The kappa4 distribution returns other distributions when certain 

5319 :math:`h` and :math:`k` values are used. 

5320 

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 +------+-------------+----------------+------------------+ 

5339 

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. 

5348 

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 

5356 

5357 J.R.M. Hosking, "The four-parameter kappa distribution". IBM J. Res. 

5358 Develop. 38 (3), 25 1-258 (1994). 

5359 

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 

5364 

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 

5369 

5370 %(after_notes)s 

5371 

5372 %(example)s 

5373 

5374 """ 

5375 def _argcheck(self, h, k): 

5376 return h == h 

5377 

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)] 

5385 

5386 def f0(h, k): 

5387 return (1.0 - float_power(h, -k))/k 

5388 

5389 def f1(h, k): 

5390 return np.log(h) 

5391 

5392 def f3(h, k): 

5393 a = np.empty(np.shape(h)) 

5394 a[:] = -np.inf 

5395 return a 

5396 

5397 def f5(h, k): 

5398 return 1.0/k 

5399 

5400 _a = _lazyselect(condlist, 

5401 [f0, f1, f0, f3, f3, f5], 

5402 [h, k], 

5403 default=np.nan) 

5404 

5405 def f0(h, k): 

5406 return 1.0/k 

5407 

5408 def f1(h, k): 

5409 a = np.empty(np.shape(h)) 

5410 a[:] = np.inf 

5411 return a 

5412 

5413 _b = _lazyselect(condlist, 

5414 [f0, f1, f1, f0, f1, f1], 

5415 [h, k], 

5416 default=np.nan) 

5417 return _a, _b 

5418 

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)) 

5423 

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)] 

5429 

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))) 

5437 

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) 

5444 

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)) 

5450 

5451 def f3(x, h, k): 

5452 '''pdf = np.exp(-x-np.exp(-x)) 

5453 logpdf = ... 

5454 ''' 

5455 return -x - np.exp(-x) 

5456 

5457 return _lazyselect(condlist, 

5458 [f0, f1, f2, f3], 

5459 [x, h, k], 

5460 default=np.nan) 

5461 

5462 def _cdf(self, x, h, k): 

5463 return np.exp(self._logcdf(x, h, k)) 

5464 

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)] 

5470 

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)) 

5476 

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) 

5482 

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)) 

5488 

5489 def f3(x, h, k): 

5490 '''cdf = np.exp(-np.exp(-x)) 

5491 logcdf = ... 

5492 ''' 

5493 return -np.exp(-x) 

5494 

5495 return _lazyselect(condlist, 

5496 [f0, f1, f2, f3], 

5497 [x, h, k], 

5498 default=np.nan) 

5499 

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)] 

5505 

5506 def f0(q, h, k): 

5507 return 1.0/k*(1.0 - ((1.0 - (q**h))/h)**k) 

5508 

5509 def f1(q, h, k): 

5510 return 1.0/k*(1.0 - (-np.log(q))**k) 

5511 

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) 

5516 

5517 def f3(q, h, k): 

5518 return -np.log(-np.log(q)) 

5519 

5520 return _lazyselect(condlist, 

5521 [f0, f1, f2, f3], 

5522 [q, h, k], 

5523 default=np.nan) 

5524 

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 

5534 

5535 outputs = [None if r < maxr else np.nan for r in range(1, 5)] 

5536 return outputs[:] 

5537 

5538 

5539kappa4 = kappa4_gen(name='kappa4') 

5540 

5541 

5542class kappa3_gen(rv_continuous): 

5543 r"""Kappa 3 parameter distribution. 

5544 

5545 %(before_notes)s 

5546 

5547 Notes 

5548 ----- 

5549 The probability density function for `kappa3` is: 

5550 

5551 .. math:: 

5552 

5553 f(x, a) = a (a + x^a)^{-(a + 1)/a} 

5554 

5555 for :math:`x > 0` and :math:`a > 0`. 

5556 

5557 `kappa3` takes ``a`` as a shape parameter for :math:`a`. 

5558 

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 

5565 

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 

5569 

5570 %(after_notes)s 

5571 

5572 %(example)s 

5573 

5574 """ 

5575 def _argcheck(self, a): 

5576 return a > 0 

5577 

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) 

5581 

5582 def _cdf(self, x, a): 

5583 return x*(a + x**a)**(-1.0/a) 

5584 

5585 def _ppf(self, q, a): 

5586 return (a/(q**-a - 1.0))**(1.0/a) 

5587 

5588 def _stats(self, a): 

5589 outputs = [None if i < a else np.nan for i in range(1, 5)] 

5590 return outputs[:] 

5591 

5592 

5593kappa3 = kappa3_gen(a=0.0, name='kappa3') 

5594 

5595class moyal_gen(rv_continuous): 

5596 r"""A Moyal continuous random variable. 

5597 

5598 %(before_notes)s 

5599 

5600 Notes 

5601 ----- 

5602 The probability density function for `moyal` is: 

5603 

5604 .. math:: 

5605 

5606 f(x) = \exp(-(x + \exp(-x))/2) / \sqrt{2\pi} 

5607 

5608 for a real number :math:`x`. 

5609 

5610 %(after_notes)s 

5611 

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]_. 

5617 

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 

5632 

5633 .. versionadded:: 1.1.0 

5634 

5635 %(example)s 

5636 

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) 

5641 

5642 def _pdf(self, x): 

5643 return np.exp(-0.5 * (x + np.exp(-x))) / np.sqrt(2*np.pi) 

5644 

5645 def _cdf(self, x): 

5646 return sc.erfc(np.exp(-0.5 * x) / np.sqrt(2)) 

5647 

5648 def _sf(self, x): 

5649 return sc.erf(np.exp(-0.5 * x) / np.sqrt(2)) 

5650 

5651 def _ppf(self, x): 

5652 return -np.log(2 * sc.erfcinv(x)**2) 

5653 

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 

5660 

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) 

5681 

5682 

5683moyal = moyal_gen(name="moyal") 

5684 

5685 

5686class nakagami_gen(rv_continuous): 

5687 r"""A Nakagami continuous random variable. 

5688 

5689 %(before_notes)s 

5690 

5691 Notes 

5692 ----- 

5693 The probability density function for `nakagami` is: 

5694 

5695 .. math:: 

5696 

5697 f(x, \nu) = \frac{2 \nu^\nu}{\Gamma(\nu)} x^{2\nu-1} \exp(-\nu x^2) 

5698 

5699 for :math:`x >= 0`, :math:`\nu > 0`. 

5700 

5701 `nakagami` takes ``nu`` as a shape parameter for :math:`\nu`. 

5702 

5703 %(after_notes)s 

5704 

5705 %(example)s 

5706 

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) 

5712 

5713 def _cdf(self, x, nu): 

5714 return sc.gammainc(nu, nu*x*x) 

5715 

5716 def _ppf(self, q, nu): 

5717 return np.sqrt(1.0/nu*sc.gammaincinv(nu, q)) 

5718 

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 

5726 

5727 

5728nakagami = nakagami_gen(a=0.0, name="nakagami") 

5729 

5730 

5731class ncx2_gen(rv_continuous): 

5732 r"""A non-central chi-squared continuous random variable. 

5733 

5734 %(before_notes)s 

5735 

5736 Notes 

5737 ----- 

5738 The probability density function for `ncx2` is: 

5739 

5740 .. math:: 

5741 

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}) 

5744 

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`). 

5750 

5751 `ncx2` takes ``df`` and ``nc`` as shape parameters. 

5752 

5753 %(after_notes)s 

5754 

5755 %(example)s 

5756 

5757 """ 

5758 def _argcheck(self, df, nc): 

5759 return (df > 0) & (nc >= 0) 

5760 

5761 def _rvs(self, df, nc, size=None, random_state=None): 

5762 return random_state.noncentral_chisquare(df, nc, size) 

5763 

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) 

5767 

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) 

5773 

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) 

5777 

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) 

5781 

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) 

5788 

5789 

5790ncx2 = ncx2_gen(a=0.0, name='ncx2') 

5791 

5792 

5793class ncf_gen(rv_continuous): 

5794 r"""A non-central F distribution continuous random variable. 

5795 

5796 %(before_notes)s 

5797 

5798 Notes 

5799 ----- 

5800 The probability density function for `ncf` is: 

5801 

5802 .. math:: 

5803 

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)} 

5815 

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. 

5821 

5822 `ncf` takes ``df1``, ``df2`` and ``nc`` as shape parameters. If ``nc=0``, 

5823 the distribution becomes equivalent to the Fisher distribution. 

5824 

5825 %(after_notes)s 

5826 

5827 See Also 

5828 -------- 

5829 scipy.stats.f : Fisher distribution 

5830 

5831 %(example)s 

5832 

5833 """ 

5834 def _argcheck(self, df1, df2, nc): 

5835 return (df1 > 0) & (df2 > 0) & (nc >= 0) 

5836 

5837 def _rvs(self, dfn, dfd, nc, size=None, random_state=None): 

5838 return random_state.noncentral_f(dfn, dfd, nc, size) 

5839 

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. 

5857 

5858 def _cdf(self, x, dfn, dfd, nc): 

5859 return sc.ncfdtr(dfn, dfd, nc, x) 

5860 

5861 def _ppf(self, q, dfn, dfd, nc): 

5862 return sc.ncfdtri(dfn, dfd, nc, q) 

5863 

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 

5870 

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) 

5879 

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) 

5884 

5885 return mu, mu2, None, None 

5886 

5887 

5888ncf = ncf_gen(a=0.0, name='ncf') 

5889 

5890 

5891class t_gen(rv_continuous): 

5892 r"""A Student's t continuous random variable. 

5893 

5894 %(before_notes)s 

5895 

5896 Notes 

5897 ----- 

5898 The probability density function for `t` is: 

5899 

5900 .. math:: 

5901 

5902 f(x, \nu) = \frac{\Gamma((\nu+1)/2)} 

5903 {\sqrt{\pi \nu} \Gamma(\nu/2)} 

5904 (1+x^2/\nu)^{-(\nu+1)/2} 

5905 

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`). 

5910 

5911 %(after_notes)s 

5912 

5913 %(example)s 

5914 

5915 """ 

5916 def _argcheck(self, df): 

5917 return df > 0 

5918 

5919 def _rvs(self, df, size=None, random_state=None): 

5920 return random_state.standard_t(df, size=size) 

5921 

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 

5930 

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 

5936 

5937 def _cdf(self, x, df): 

5938 return sc.stdtr(df, x) 

5939 

5940 def _sf(self, x, df): 

5941 return sc.stdtr(df, -x) 

5942 

5943 def _ppf(self, q, df): 

5944 return sc.stdtrit(df, q) 

5945 

5946 def _isf(self, q, df): 

5947 return -sc.stdtrit(df, q) 

5948 

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 

5961 

5962 

5963t = t_gen(name='t') 

5964 

5965 

5966class nct_gen(rv_continuous): 

5967 r"""A non-central Student's t continuous random variable. 

5968 

5969 %(before_notes)s 

5970 

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 

5976 

5977 .. math:: 

5978 

5979 X = \frac{Y + c}{\sqrt{V/k}} 

5980 

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. 

5985 

5986 %(after_notes)s 

5987 

5988 %(example)s 

5989 

5990 """ 

5991 def _argcheck(self, df, nc): 

5992 return (df > 0) & (nc == nc) 

5993 

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) 

5998 

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 

6015 

6016 def _cdf(self, x, df, nc): 

6017 return sc.nctdtr(df, nc, x) 

6018 

6019 def _ppf(self, q, df, nc): 

6020 return sc.nctdtrit(df, nc, q) 

6021 

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 

6030 

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.) 

6050 

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 

6054 

6055 

6056nct = nct_gen(name="nct") 

6057 

6058 

6059class pareto_gen(rv_continuous): 

6060 r"""A Pareto continuous random variable. 

6061 

6062 %(before_notes)s 

6063 

6064 Notes 

6065 ----- 

6066 The probability density function for `pareto` is: 

6067 

6068 .. math:: 

6069 

6070 f(x, b) = \frac{b}{x^{b+1}} 

6071 

6072 for :math:`x \ge 1`, :math:`b > 0`. 

6073 

6074 `pareto` takes ``b`` as a shape parameter for :math:`b`. 

6075 

6076 %(after_notes)s 

6077 

6078 %(example)s 

6079 

6080 """ 

6081 def _pdf(self, x, b): 

6082 # pareto.pdf(x, b) = b / x**(b+1) 

6083 return b * x**(-b-1) 

6084 

6085 def _cdf(self, x, b): 

6086 return 1 - x**(-b) 

6087 

6088 def _ppf(self, q, b): 

6089 return pow(1-q, -1.0/b) 

6090 

6091 def _sf(self, x, b): 

6092 return x**(-b) 

6093 

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 

6120 

6121 def _entropy(self, c): 

6122 return 1 + 1.0/c - np.log(c) 

6123 

6124 

6125pareto = pareto_gen(a=1.0, name="pareto") 

6126 

6127 

6128class lomax_gen(rv_continuous): 

6129 r"""A Lomax (Pareto of the second kind) continuous random variable. 

6130 

6131 %(before_notes)s 

6132 

6133 Notes 

6134 ----- 

6135 The probability density function for `lomax` is: 

6136 

6137 .. math:: 

6138 

6139 f(x, c) = \frac{c}{(1+x)^{c+1}} 

6140 

6141 for :math:`x \ge 0`, :math:`c > 0`. 

6142 

6143 `lomax` takes ``c`` as a shape parameter for :math:`c`. 

6144 

6145 `lomax` is a special case of `pareto` with ``loc=-1.0``. 

6146 

6147 %(after_notes)s 

6148 

6149 %(example)s 

6150 

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) 

6155 

6156 def _logpdf(self, x, c): 

6157 return np.log(c) - (c+1)*sc.log1p(x) 

6158 

6159 def _cdf(self, x, c): 

6160 return -sc.expm1(-c*sc.log1p(x)) 

6161 

6162 def _sf(self, x, c): 

6163 return np.exp(-c*sc.log1p(x)) 

6164 

6165 def _logsf(self, x, c): 

6166 return -c*sc.log1p(x) 

6167 

6168 def _ppf(self, q, c): 

6169 return sc.expm1(-sc.log1p(-q)/c) 

6170 

6171 def _stats(self, c): 

6172 mu, mu2, g1, g2 = pareto.stats(c, loc=-1.0, moments='mvsk') 

6173 return mu, mu2, g1, g2 

6174 

6175 def _entropy(self, c): 

6176 return 1+1.0/c-np.log(c) 

6177 

6178 

6179lomax = lomax_gen(a=0.0, name="lomax") 

6180 

6181 

6182class pearson3_gen(rv_continuous): 

6183 r"""A pearson type III continuous random variable. 

6184 

6185 %(before_notes)s 

6186 

6187 Notes 

6188 ----- 

6189 The probability density function for `pearson3` is: 

6190 

6191 .. math:: 

6192 

6193 f(x, skew) = \frac{|\beta|}{\Gamma(\alpha)} 

6194 (\beta (x - \zeta))^{\alpha - 1} 

6195 \exp(-\beta (x - \zeta)) 

6196 

6197 where: 

6198 

6199 .. math:: 

6200 

6201 \beta = \frac{2}{skew stddev} 

6202 \alpha = (stddev \beta)^2 

6203 \zeta = loc - \frac{\alpha}{\beta} 

6204 

6205 :math:`\Gamma` is the gamma function (`scipy.special.gamma`). 

6206 `pearson3` takes ``skew`` as a shape parameter for :math:`skew`. 

6207 

6208 %(after_notes)s 

6209 

6210 %(example)s 

6211 

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). 

6217 

6218 L.R. Salvosa, "Tables of Pearson's Type III Function", Ann. Math. Statist., 

6219 Vol.1, 191-198 (1930). 

6220 

6221 "Using Modern Computing Tools to Fit the Pearson Type III Distribution to 

6222 Aviation Loads Data", Office of Aviation Research (2003). 

6223 

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 

6232 

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 

6238 

6239 ans, x, skew = np.broadcast_arrays([1.0], x, skew) 

6240 ans = ans.copy() 

6241 

6242 # mask is True where skew is small enough to use the normal approx. 

6243 mask = np.absolute(skew) < norm2pearson_transition 

6244 invmask = ~mask 

6245 

6246 beta = 2.0 / (skew[invmask] * scale) 

6247 alpha = (scale * beta)**2 

6248 zeta = loc - alpha / beta 

6249 

6250 transx = beta * (x[invmask] - zeta) 

6251 return ans, x, transx, mask, invmask, beta, alpha, zeta 

6252 

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) 

6259 

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 

6268 

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 

6281 

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)) 

6290 

6291 ans[mask] = np.log(_norm_pdf(x[mask])) 

6292 ans[invmask] = np.log(abs(beta)) + gamma._logpdf(transx, alpha) 

6293 return ans 

6294 

6295 def _cdf(self, x, skew): 

6296 ans, x, transx, mask, invmask, _, alpha, _ = ( 

6297 self._preprocess(x, skew)) 

6298 

6299 ans[mask] = _norm_cdf(x[mask]) 

6300 ans[invmask] = gamma._cdf(transx, alpha) 

6301 return ans 

6302 

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)) 

6307 

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 

6312 

6313 if size == (): 

6314 ans = ans[0] 

6315 return ans 

6316 

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 

6323 

6324 

6325pearson3 = pearson3_gen(name="pearson3") 

6326 

6327 

6328class powerlaw_gen(rv_continuous): 

6329 r"""A power-function continuous random variable. 

6330 

6331 %(before_notes)s 

6332 

6333 Notes 

6334 ----- 

6335 The probability density function for `powerlaw` is: 

6336 

6337 .. math:: 

6338 

6339 f(x, a) = a x^{a-1} 

6340 

6341 for :math:`0 \le x \le 1`, :math:`a > 0`. 

6342 

6343 `powerlaw` takes ``a`` as a shape parameter for :math:`a`. 

6344 

6345 %(after_notes)s 

6346 

6347 `powerlaw` is a special case of `beta` with ``b=1``. 

6348 

6349 %(example)s 

6350 

6351 """ 

6352 def _pdf(self, x, a): 

6353 # powerlaw.pdf(x, a) = a * x**(a-1) 

6354 return a*x**(a-1.0) 

6355 

6356 def _logpdf(self, x, a): 

6357 return np.log(a) + sc.xlogy(a - 1, x) 

6358 

6359 def _cdf(self, x, a): 

6360 return x**(a*1.0) 

6361 

6362 def _logcdf(self, x, a): 

6363 return a*np.log(x) 

6364 

6365 def _ppf(self, q, a): 

6366 return pow(q, 1.0/a) 

6367 

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))) 

6373 

6374 def _entropy(self, a): 

6375 return 1 - 1.0/a - np.log(a) 

6376 

6377 

6378powerlaw = powerlaw_gen(a=0.0, b=1.0, name="powerlaw") 

6379 

6380 

6381class powerlognorm_gen(rv_continuous): 

6382 r"""A power log-normal continuous random variable. 

6383 

6384 %(before_notes)s 

6385 

6386 Notes 

6387 ----- 

6388 The probability density function for `powerlognorm` is: 

6389 

6390 .. math:: 

6391 

6392 f(x, c, s) = \frac{c}{x s} \phi(\log(x)/s) 

6393 (\Phi(-\log(x)/s))^{c-1} 

6394 

6395 where :math:`\phi` is the normal pdf, and :math:`\Phi` is the normal cdf, 

6396 and :math:`x > 0`, :math:`s, c > 0`. 

6397 

6398 `powerlognorm` takes :math:`c` and :math:`s` as shape parameters. 

6399 

6400 %(after_notes)s 

6401 

6402 %(example)s 

6403 

6404 """ 

6405 _support_mask = rv_continuous._open_support_mask 

6406 

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)) 

6412 

6413 def _cdf(self, x, c, s): 

6414 return 1.0 - pow(_norm_cdf(-np.log(x)/s), c*1.0) 

6415 

6416 def _ppf(self, q, c, s): 

6417 return np.exp(-s * _norm_ppf(pow(1.0 - q, 1.0 / c))) 

6418 

6419 

6420powerlognorm = powerlognorm_gen(a=0.0, name="powerlognorm") 

6421 

6422 

6423class powernorm_gen(rv_continuous): 

6424 r"""A power normal continuous random variable. 

6425 

6426 %(before_notes)s 

6427 

6428 Notes 

6429 ----- 

6430 The probability density function for `powernorm` is: 

6431 

6432 .. math:: 

6433 

6434 f(x, c) = c \phi(x) (\Phi(-x))^{c-1} 

6435 

6436 where :math:`\phi` is the normal pdf, and :math:`\Phi` is the normal cdf, 

6437 and :math:`x >= 0`, :math:`c > 0`. 

6438 

6439 `powernorm` takes ``c`` as a shape parameter for :math:`c`. 

6440 

6441 %(after_notes)s 

6442 

6443 %(example)s 

6444 

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)) 

6449 

6450 def _logpdf(self, x, c): 

6451 return np.log(c) + _norm_logpdf(x) + (c-1)*_norm_logcdf(-x) 

6452 

6453 def _cdf(self, x, c): 

6454 return 1.0-_norm_cdf(-x)**(c*1.0) 

6455 

6456 def _ppf(self, q, c): 

6457 return -_norm_ppf(pow(1.0 - q, 1.0 / c)) 

6458 

6459 

6460powernorm = powernorm_gen(name='powernorm') 

6461 

6462 

6463class rdist_gen(rv_continuous): 

6464 r"""An R-distributed (symmetric beta) continuous random variable. 

6465 

6466 %(before_notes)s 

6467 

6468 Notes 

6469 ----- 

6470 The probability density function for `rdist` is: 

6471 

6472 .. math:: 

6473 

6474 f(x, c) = \frac{(1-x^2)^{c/2-1}}{B(1/2, c/2)} 

6475 

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. 

6480 

6481 `rdist` takes ``c`` as a shape parameter for :math:`c`. 

6482 

6483 This distribution includes the following distribution kernels as 

6484 special cases:: 

6485 

6486 c = 2: uniform 

6487 c = 3: `semicircular` 

6488 c = 4: Epanechnikov (parabolic) 

6489 c = 6: quartic (biweight) 

6490 c = 8: triweight 

6491 

6492 %(after_notes)s 

6493 

6494 %(example)s 

6495 

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) 

6500 

6501 def _logpdf(self, x, c): 

6502 return -np.log(2) + beta._logpdf((x + 1)/2, c/2, c/2) 

6503 

6504 def _cdf(self, x, c): 

6505 return beta._cdf((x + 1)/2, c/2, c/2) 

6506 

6507 def _ppf(self, q, c): 

6508 return 2*beta._ppf(q, c/2, c/2) - 1 

6509 

6510 def _rvs(self, c, size=None, random_state=None): 

6511 return 2 * random_state.beta(c/2, c/2, size) - 1 

6512 

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.) 

6516 

6517 

6518rdist = rdist_gen(a=-1.0, b=1.0, name="rdist") 

6519 

6520 

6521class rayleigh_gen(rv_continuous): 

6522 r"""A Rayleigh continuous random variable. 

6523 

6524 %(before_notes)s 

6525 

6526 Notes 

6527 ----- 

6528 The probability density function for `rayleigh` is: 

6529 

6530 .. math:: 

6531 

6532 f(x) = x \exp(-x^2/2) 

6533 

6534 for :math:`x \ge 0`. 

6535 

6536 `rayleigh` is a special case of `chi` with ``df=2``. 

6537 

6538 %(after_notes)s 

6539 

6540 %(example)s 

6541 

6542 """ 

6543 _support_mask = rv_continuous._open_support_mask 

6544 

6545 def _rvs(self, size=None, random_state=None): 

6546 return chi.rvs(2, size=size, random_state=random_state) 

6547 

6548 def _pdf(self, r): 

6549 # rayleigh.pdf(r) = r * exp(-r**2/2) 

6550 return np.exp(self._logpdf(r)) 

6551 

6552 def _logpdf(self, r): 

6553 return np.log(r) - 0.5 * r * r 

6554 

6555 def _cdf(self, r): 

6556 return -sc.expm1(-0.5 * r**2) 

6557 

6558 def _ppf(self, q): 

6559 return np.sqrt(-2 * sc.log1p(-q)) 

6560 

6561 def _sf(self, r): 

6562 return np.exp(self._logsf(r)) 

6563 

6564 def _logsf(self, r): 

6565 return -0.5 * r * r 

6566 

6567 def _isf(self, q): 

6568 return np.sqrt(-2 * np.log(q)) 

6569 

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) 

6576 

6577 def _entropy(self): 

6578 return _EULER/2.0 + 1 - 0.5*np.log(2) 

6579 

6580 

6581rayleigh = rayleigh_gen(a=0.0, name="rayleigh") 

6582 

6583 

6584class reciprocal_gen(rv_continuous): 

6585 r"""A loguniform or reciprocal continuous random variable. 

6586 

6587 %(before_notes)s 

6588 

6589 Notes 

6590 ----- 

6591 The probability density function for this class is: 

6592 

6593 .. math:: 

6594 

6595 f(x, a, b) = \frac{1}{x \log(b/a)} 

6596 

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 

6599 

6600 %(example)s 

6601 

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: 

6604 

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() 

6614 

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: 

6617 

6618 >>> rvs = %(name)s(2**-2, 2**0).rvs(size=1000) 

6619 

6620 Values of ``1/4``, ``1/2`` and ``1`` are equally likely with this random 

6621 variable. Here's the histogram: 

6622 

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() 

6631 

6632 """ 

6633 def _argcheck(self, a, b): 

6634 return (a > 0) & (b > a) 

6635 

6636 def _get_support(self, a, b): 

6637 return a, b 

6638 

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)) 

6642 

6643 def _logpdf(self, x, a, b): 

6644 return -np.log(x) - np.log(np.log(b * 1.0 / a)) 

6645 

6646 def _cdf(self, x, a, b): 

6647 return (np.log(x)-np.log(a)) / np.log(b * 1.0 / a) 

6648 

6649 def _ppf(self, q, a, b): 

6650 return a*pow(b*1.0/a, q) 

6651 

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)) 

6654 

6655 def _entropy(self, a, b): 

6656 return 0.5*np.log(a*b)+np.log(np.log(b*1.0/a)) 

6657 

6658 

6659loguniform = reciprocal_gen(name="loguniform") 

6660reciprocal = reciprocal_gen(name="reciprocal") 

6661 

6662 

6663class rice_gen(rv_continuous): 

6664 r"""A Rice continuous random variable. 

6665 

6666 %(before_notes)s 

6667 

6668 Notes 

6669 ----- 

6670 The probability density function for `rice` is: 

6671 

6672 .. math:: 

6673 

6674 f(x, b) = x \exp(- \frac{x^2 + b^2}{2}) I_0(x b) 

6675 

6676 for :math:`x >= 0`, :math:`b > 0`. :math:`I_0` is the modified Bessel 

6677 function of order zero (`scipy.special.i0`). 

6678 

6679 `rice` takes ``b`` as a shape parameter for :math:`b`. 

6680 

6681 %(after_notes)s 

6682 

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)``. 

6688 

6689 %(example)s 

6690 

6691 """ 

6692 def _argcheck(self, b): 

6693 return b >= 0 

6694 

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)) 

6699 

6700 def _cdf(self, x, b): 

6701 return sc.chndtr(np.square(x), 2, np.square(b)) 

6702 

6703 def _ppf(self, q, b): 

6704 return np.sqrt(sc.chndtrix(q, 2, np.square(b))) 

6705 

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) 

6714 

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)) 

6721 

6722 

6723rice = rice_gen(a=0.0, name="rice") 

6724 

6725 

6726# FIXME: PPF does not work. 

6727class recipinvgauss_gen(rv_continuous): 

6728 r"""A reciprocal inverse Gaussian continuous random variable. 

6729 

6730 %(before_notes)s 

6731 

6732 Notes 

6733 ----- 

6734 The probability density function for `recipinvgauss` is: 

6735 

6736 .. math:: 

6737 

6738 f(x, \mu) = \frac{1}{\sqrt{2\pi x}} 

6739 \exp\left(\frac{-(1-\mu x)^2}{2\mu^2x}\right) 

6740 

6741 for :math:`x \ge 0`. 

6742 

6743 `recipinvgauss` takes ``mu`` as a shape parameter for :math:`\mu`. 

6744 

6745 %(after_notes)s 

6746 

6747 %(example)s 

6748 

6749 """ 

6750 

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)) 

6755 

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) 

6758 

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) 

6764 

6765 def _rvs(self, mu, size=None, random_state=None): 

6766 return 1.0/random_state.wald(mu, 1.0, size=size) 

6767 

6768 

6769recipinvgauss = recipinvgauss_gen(a=0.0, name='recipinvgauss') 

6770 

6771 

6772class semicircular_gen(rv_continuous): 

6773 r"""A semicircular continuous random variable. 

6774 

6775 %(before_notes)s 

6776 

6777 Notes 

6778 ----- 

6779 The probability density function for `semicircular` is: 

6780 

6781 .. math:: 

6782 

6783 f(x) = \frac{2}{\pi} \sqrt{1-x^2} 

6784 

6785 for :math:`-1 \le x \le 1`. 

6786 

6787 The distribution is a special case of `rdist` with `c = 3`. 

6788 

6789 %(after_notes)s 

6790 

6791 See Also 

6792 -------- 

6793 rdist 

6794 

6795 References 

6796 ---------- 

6797 .. [1] "Wigner semicircle distribution", 

6798 https://en.wikipedia.org/wiki/Wigner_semicircle_distribution 

6799 

6800 %(example)s 

6801 

6802 """ 

6803 def _pdf(self, x): 

6804 return 2.0/np.pi*np.sqrt(1-x*x) 

6805 

6806 def _logpdf(self, x): 

6807 return np.log(2/np.pi) + 0.5*np.log1p(-x*x) 

6808 

6809 def _cdf(self, x): 

6810 return 0.5+1.0/np.pi*(x*np.sqrt(1-x*x) + np.arcsin(x)) 

6811 

6812 def _ppf(self, q): 

6813 return rdist._ppf(q, 3) 

6814 

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 

6821 

6822 def _stats(self): 

6823 return 0, 0.25, 0, -1.0 

6824 

6825 def _entropy(self): 

6826 return 0.64472988584940017414 

6827 

6828 

6829semicircular = semicircular_gen(a=-1.0, b=1.0, name="semicircular") 

6830 

6831 

6832class skew_norm_gen(rv_continuous): 

6833 r"""A skew-normal random variable. 

6834 

6835 %(before_notes)s 

6836 

6837 Notes 

6838 ----- 

6839 The pdf is:: 

6840 

6841 skewnorm.pdf(x, a) = 2 * norm.pdf(x) * norm.cdf(a*x) 

6842 

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]_. 

6846 

6847 %(after_notes)s 

6848 

6849 %(example)s 

6850 

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 

6856 

6857 """ 

6858 def _argcheck(self, a): 

6859 return np.isfinite(a) 

6860 

6861 def _pdf(self, x, a): 

6862 return 2.*_norm_pdf(x)*_norm_cdf(a*x) 

6863 

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 

6876 

6877 def _sf(self, x, a): 

6878 return self._cdf(-x, -a) 

6879 

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) 

6886 

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) 

6890 

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) 

6899 

6900 return output 

6901 

6902 

6903skewnorm = skew_norm_gen(name='skewnorm') 

6904 

6905 

6906class trapz_gen(rv_continuous): 

6907 r"""A trapezoidal continuous random variable. 

6908 

6909 %(before_notes)s 

6910 

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. 

6921 

6922 `trapz` takes :math:`c` and :math:`d` as shape parameters. 

6923 

6924 %(after_notes)s 

6925 

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`. 

6929 

6930 %(example)s 

6931 

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 

6937 

6938 

6939 """ 

6940 def _argcheck(self, c, d): 

6941 return (c >= 0) & (c <= 1) & (d >= 0) & (d <= 1) & (d >= c) 

6942 

6943 def _pdf(self, x, c, d): 

6944 u = 2 / (d-c+1) 

6945 

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)) 

6953 

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)) 

6963 

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) 

6971 

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 

6996 

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)) 

7005 

7006 

7007trapz = trapz_gen(a=0.0, b=1.0, name="trapz") 

7008 

7009 

7010class triang_gen(rv_continuous): 

7011 r"""A triangular continuous random variable. 

7012 

7013 %(before_notes)s 

7014 

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)``. 

7020 

7021 `triang` takes ``c`` as a shape parameter for :math:`c`. 

7022 

7023 %(after_notes)s 

7024 

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`. 

7028 

7029 %(example)s 

7030 

7031 """ 

7032 def _rvs(self, c, size=None, random_state=None): 

7033 return random_state.triangular(0, c, 1, size) 

7034 

7035 def _argcheck(self, c): 

7036 return (c >= 0) & (c <= 1) 

7037 

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 

7054 

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 

7066 

7067 def _ppf(self, q, c): 

7068 return np.where(q < c, np.sqrt(c * q), 1-np.sqrt((1-c) * (1-q))) 

7069 

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) 

7075 

7076 def _entropy(self, c): 

7077 return 0.5-np.log(2) 

7078 

7079 

7080triang = triang_gen(a=0.0, b=1.0, name="triang") 

7081 

7082 

7083class truncexpon_gen(rv_continuous): 

7084 r"""A truncated exponential continuous random variable. 

7085 

7086 %(before_notes)s 

7087 

7088 Notes 

7089 ----- 

7090 The probability density function for `truncexpon` is: 

7091 

7092 .. math:: 

7093 

7094 f(x, b) = \frac{\exp(-x)}{1 - \exp(-b)} 

7095 

7096 for :math:`0 <= x <= b`. 

7097 

7098 `truncexpon` takes ``b`` as a shape parameter for :math:`b`. 

7099 

7100 %(after_notes)s 

7101 

7102 %(example)s 

7103 

7104 """ 

7105 def _argcheck(self, b): 

7106 return b > 0 

7107 

7108 def _get_support(self, b): 

7109 return self.a, b 

7110 

7111 def _pdf(self, x, b): 

7112 # truncexpon.pdf(x, b) = exp(-x) / (1-exp(-b)) 

7113 return np.exp(-x)/(-sc.expm1(-b)) 

7114 

7115 def _logpdf(self, x, b): 

7116 return -x - np.log(-sc.expm1(-b)) 

7117 

7118 def _cdf(self, x, b): 

7119 return sc.expm1(-x)/sc.expm1(-b) 

7120 

7121 def _ppf(self, q, b): 

7122 return -sc.log1p(q*sc.expm1(-b)) 

7123 

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) 

7135 

7136 def _entropy(self, b): 

7137 eB = np.exp(b) 

7138 return np.log(eB-1)+(1+eB*(b-1.0))/(1.0-eB) 

7139 

7140 

7141truncexpon = truncexpon_gen(a=0.0, name='truncexpon') 

7142 

7143 

7144TRUNCNORM_TAIL_X = 30 

7145TRUNCNORM_MAX_BRENT_ITERS = 40 

7146 

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 

7156 

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 

7173 

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) 

7183 

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 

7191 

7192 

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) 

7213 

7214 

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) 

7239 

7240 

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) 

7277 

7278 

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) 

7305 

7306 

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) 

7317 

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) 

7342 

7343 

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) 

7354 

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) 

7368 

7369 

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) 

7391 

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 

7402 

7403 

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 

7438 

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 

7454 

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) 

7467 

7468 

7469class truncnorm_gen(rv_continuous): 

7470 r"""A truncated normal continuous random variable. 

7471 

7472 %(before_notes)s 

7473 

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:: 

7480 

7481 a, b = (myclip_a - my_mean) / my_std, (myclip_b - my_mean) / my_std 

7482 

7483 `truncnorm` takes :math:`a` and :math:`b` as shape parameters. 

7484 

7485 %(after_notes)s 

7486 

7487 %(example)s 

7488 

7489 """ 

7490 def _argcheck(self, a, b): 

7491 return a < b 

7492 

7493 def _get_support(self, a, b): 

7494 return a, b 

7495 

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] 

7507 

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] 

7519 

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] 

7532 

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] 

7544 

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] 

7557 

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] 

7570 

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()) 

7577 

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] 

7584 

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] 

7604 

7605 return _lazywhere((n >= 0) & (a == a) & (b == b), (n, a, b), 

7606 np.vectorize(n_th_moment, otypes=[np.float]), np.nan) 

7607 

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) 

7628 

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 

7634 

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 (). 

7646 

7647 a, b = np.broadcast_arrays(a, b) 

7648 # a and b now have the same shape. 

7649 

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) 

7658 

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)) 

7662 

7663 # `out` is the array to be returned. It is filled in in the 

7664 # loop below. 

7665 out = np.empty(size) 

7666 

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() 

7684 

7685 if size == (): 

7686 out = out[()] 

7687 return out 

7688 

7689 def _rvs_scalar(self, a, b, numsamples=None, random_state=None): 

7690 if not numsamples: 

7691 numsamples = 1 

7692 

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 

7701 

7702 

7703truncnorm = truncnorm_gen(name='truncnorm', momtype=1) 

7704 

7705 

7706# FIXME: RVS does not work. 

7707class tukeylambda_gen(rv_continuous): 

7708 r"""A Tukey-Lamdba continuous random variable. 

7709 

7710 %(before_notes)s 

7711 

7712 Notes 

7713 ----- 

7714 A flexible distribution, able to represent and interpolate between the 

7715 following distributions: 

7716 

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`) 

7721 

7722 `tukeylambda` takes a real number :math:`lambda` (denoted ``lam`` 

7723 in the implementation) as a shape parameter. 

7724 

7725 %(after_notes)s 

7726 

7727 %(example)s 

7728 

7729 """ 

7730 def _argcheck(self, lam): 

7731 return np.ones(np.shape(lam), dtype=bool) 

7732 

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) 

7738 

7739 def _cdf(self, x, lam): 

7740 return sc.tklmbda(x, lam) 

7741 

7742 def _ppf(self, q, lam): 

7743 return sc.boxcox(q, lam) - sc.boxcox1p(-q, lam) 

7744 

7745 def _stats(self, lam): 

7746 return 0, _tlvar(lam), 0, _tlkurt(lam) 

7747 

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] 

7752 

7753 

7754tukeylambda = tukeylambda_gen(name='tukeylambda') 

7755 

7756 

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 ) 

7765 

7766 

7767class uniform_gen(rv_continuous): 

7768 r"""A uniform continuous random variable. 

7769 

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]``. 

7773 

7774 %(before_notes)s 

7775 

7776 %(example)s 

7777 

7778 """ 

7779 def _rvs(self, size=None, random_state=None): 

7780 return random_state.uniform(0.0, 1.0, size) 

7781 

7782 def _pdf(self, x): 

7783 return 1.0*(x == x) 

7784 

7785 def _cdf(self, x): 

7786 return x 

7787 

7788 def _ppf(self, q): 

7789 return q 

7790 

7791 def _stats(self): 

7792 return 0.5, 1.0/12, 0, -1.2 

7793 

7794 def _entropy(self): 

7795 return 0.0 

7796 

7797 def fit(self, data, *args, **kwds): 

7798 """ 

7799 Maximum likelihood estimate for the location and scale parameters. 

7800 

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`. 

7805 

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. 

7814 

7815 Returns 

7816 ------- 

7817 loc, scale : float 

7818 Maximum likelihood estimates for the location and scale. 

7819 

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. 

7826 

7827 Examples 

7828 -------- 

7829 >>> from scipy.stats import uniform 

7830 

7831 We'll fit the uniform distribution to `x`: 

7832 

7833 >>> x = np.array([2, 2.5, 3.1, 9.5, 13.0]) 

7834 

7835 For a uniform distribution MLE, the location is the minimum of the 

7836 data, and the scale is the maximum minus the minimum. 

7837 

7838 >>> loc, scale = uniform.fit(x) 

7839 >>> loc 

7840 2.0 

7841 >>> scale 

7842 11.0 

7843 

7844 If we know the data comes from a uniform distribution where the support 

7845 starts at 0, we can use `floc=0`: 

7846 

7847 >>> loc, scale = uniform.fit(x, floc=0) 

7848 >>> loc 

7849 0.0 

7850 >>> scale 

7851 13.0 

7852 

7853 Alternatively, if we know the length of the support is 12, we can use 

7854 `fscale=12`: 

7855 

7856 >>> loc, scale = uniform.fit(x, fscale=12) 

7857 >>> loc 

7858 1.5 

7859 >>> scale 

7860 12.0 

7861 

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()]``. 

7868 

7869 """ 

7870 if len(args) > 0: 

7871 raise TypeError("Too many arguments.") 

7872 

7873 floc = kwds.pop('floc', None) 

7874 fscale = kwds.pop('fscale', None) 

7875 

7876 _remove_optimizer_parameters(kwds) 

7877 

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.") 

7882 

7883 data = np.asarray(data) 

7884 

7885 if not np.isfinite(data).all(): 

7886 raise RuntimeError("The data contains non-finite values.") 

7887 

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()) 

7916 

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 

7939 

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) 

7943 

7944 

7945uniform = uniform_gen(a=0.0, b=1.0, name='uniform') 

7946 

7947 

7948class vonmises_gen(rv_continuous): 

7949 r"""A Von Mises continuous random variable. 

7950 

7951 %(before_notes)s 

7952 

7953 Notes 

7954 ----- 

7955 The probability density function for `vonmises` and `vonmises_line` is: 

7956 

7957 .. math:: 

7958 

7959 f(x, \kappa) = \frac{ \exp(\kappa \cos(x)) }{ 2 \pi I_0(\kappa) } 

7960 

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`). 

7963 

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``. 

7968 

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. 

7971 

7972 `vonmises` and `vonmises_line` take ``kappa`` as a shape parameter. 

7973 

7974 %(after_notes)s 

7975 

7976 %(example)s 

7977 

7978 """ 

7979 def _rvs(self, kappa, size=None, random_state=None): 

7980 return random_state.vonmises(0.0, kappa, size=size) 

7981 

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)) 

7985 

7986 def _cdf(self, x, kappa): 

7987 return _stats.von_mises_cdf(kappa, x) 

7988 

7989 def _stats_skip(self, kappa): 

7990 return 0, None, 0, None 

7991 

7992 def _entropy(self, kappa): 

7993 return (-kappa * sc.i1(kappa) / sc.i0(kappa) + 

7994 np.log(2 * np.pi * sc.i0(kappa))) 

7995 

7996 

7997vonmises = vonmises_gen(name='vonmises') 

7998vonmises_line = vonmises_gen(a=-np.pi, b=np.pi, name='vonmises_line') 

7999 

8000 

8001class wald_gen(invgauss_gen): 

8002 r"""A Wald continuous random variable. 

8003 

8004 %(before_notes)s 

8005 

8006 Notes 

8007 ----- 

8008 The probability density function for `wald` is: 

8009 

8010 .. math:: 

8011 

8012 f(x) = \frac{1}{\sqrt{2\pi x^3}} \exp(- \frac{ (x-1)^2 }{ 2x }) 

8013 

8014 for :math:`x >= 0`. 

8015 

8016 `wald` is a special case of `invgauss` with ``mu=1``. 

8017 

8018 %(after_notes)s 

8019 

8020 %(example)s 

8021 """ 

8022 _support_mask = rv_continuous._open_support_mask 

8023 

8024 def _rvs(self, size=None, random_state=None): 

8025 return random_state.wald(1.0, 1.0, size=size) 

8026 

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) 

8030 

8031 def _logpdf(self, x): 

8032 return invgauss._logpdf(x, 1.0) 

8033 

8034 def _cdf(self, x): 

8035 return invgauss._cdf(x, 1.0) 

8036 

8037 def _stats(self): 

8038 return 1.0, 1.0, 3.0, 15.0 

8039 

8040 

8041wald = wald_gen(a=0.0, name="wald") 

8042 

8043 

8044class wrapcauchy_gen(rv_continuous): 

8045 r"""A wrapped Cauchy continuous random variable. 

8046 

8047 %(before_notes)s 

8048 

8049 Notes 

8050 ----- 

8051 The probability density function for `wrapcauchy` is: 

8052 

8053 .. math:: 

8054 

8055 f(x, c) = \frac{1-c^2}{2\pi (1+c^2 - 2c \cos(x))} 

8056 

8057 for :math:`0 \le x \le 2\pi`, :math:`0 < c < 1`. 

8058 

8059 `wrapcauchy` takes ``c`` as a shape parameter for :math:`c`. 

8060 

8061 %(after_notes)s 

8062 

8063 %(example)s 

8064 

8065 """ 

8066 def _argcheck(self, c): 

8067 return (c > 0) & (c < 1) 

8068 

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))) 

8072 

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 

8092 

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) 

8098 

8099 def _entropy(self, c): 

8100 return np.log(2*np.pi*(1-c*c)) 

8101 

8102 

8103wrapcauchy = wrapcauchy_gen(a=0.0, b=2*np.pi, name='wrapcauchy') 

8104 

8105 

8106class gennorm_gen(rv_continuous): 

8107 r"""A generalized normal continuous random variable. 

8108 

8109 %(before_notes)s 

8110 

8111 Notes 

8112 ----- 

8113 The probability density function for `gennorm` is [1]_: 

8114 

8115 .. math:: 

8116 

8117 f(x, \beta) = \frac{\beta}{2 \Gamma(1/\beta)} \exp(-|x|^\beta) 

8118 

8119 :math:`\Gamma` is the gamma function (`scipy.special.gamma`). 

8120 

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)``). 

8125 

8126 See Also 

8127 -------- 

8128 laplace : Laplace distribution 

8129 norm : normal distribution 

8130 

8131 References 

8132 ---------- 

8133 

8134 .. [1] "Generalized normal distribution, Version 1", 

8135 https://en.wikipedia.org/wiki/Generalized_normal_distribution#Version_1 

8136 

8137 %(example)s 

8138 

8139 """ 

8140 

8141 def _pdf(self, x, beta): 

8142 return np.exp(self._logpdf(x, beta)) 

8143 

8144 def _logpdf(self, x, beta): 

8145 return np.log(0.5*beta) - sc.gammaln(1.0/beta) - abs(x)**beta 

8146 

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) 

8151 

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) 

8156 

8157 def _sf(self, x, beta): 

8158 return self._cdf(-x, beta) 

8159 

8160 def _isf(self, x, beta): 

8161 return -self._ppf(x, beta) 

8162 

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. 

8166 

8167 def _entropy(self, beta): 

8168 return 1. / beta - np.log(.5 * beta) + sc.gammaln(1. / beta) 

8169 

8170 

8171gennorm = gennorm_gen(name='gennorm') 

8172 

8173 

8174class halfgennorm_gen(rv_continuous): 

8175 r"""The upper half of a generalized normal continuous random variable. 

8176 

8177 %(before_notes)s 

8178 

8179 Notes 

8180 ----- 

8181 The probability density function for `halfgennorm` is: 

8182 

8183 .. math:: 

8184 

8185 f(x, \beta) = \frac{\beta}{\Gamma(1/\beta)} \exp(-|x|^\beta) 

8186 

8187 for :math:`x > 0`. :math:`\Gamma` is the gamma function 

8188 (`scipy.special.gamma`). 

8189 

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)``). 

8194 

8195 See Also 

8196 -------- 

8197 gennorm : generalized normal distribution 

8198 expon : exponential distribution 

8199 halfnorm : half normal distribution 

8200 

8201 References 

8202 ---------- 

8203 

8204 .. [1] "Generalized normal distribution, Version 1", 

8205 https://en.wikipedia.org/wiki/Generalized_normal_distribution#Version_1 

8206 

8207 %(example)s 

8208 

8209 """ 

8210 

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)) 

8216 

8217 def _logpdf(self, x, beta): 

8218 return np.log(beta) - sc.gammaln(1.0/beta) - x**beta 

8219 

8220 def _cdf(self, x, beta): 

8221 return sc.gammainc(1.0/beta, x**beta) 

8222 

8223 def _ppf(self, x, beta): 

8224 return sc.gammaincinv(1.0/beta, x)**(1.0/beta) 

8225 

8226 def _sf(self, x, beta): 

8227 return sc.gammaincc(1.0/beta, x**beta) 

8228 

8229 def _isf(self, x, beta): 

8230 return sc.gammainccinv(1.0/beta, x)**(1.0/beta) 

8231 

8232 def _entropy(self, beta): 

8233 return 1.0/beta - np.log(beta) + sc.gammaln(1.0/beta) 

8234 

8235 

8236halfgennorm = halfgennorm_gen(a=0, name='halfgennorm') 

8237 

8238 

8239class crystalball_gen(rv_continuous): 

8240 r""" 

8241 Crystalball distribution 

8242 

8243 %(before_notes)s 

8244 

8245 Notes 

8246 ----- 

8247 The probability density function for `crystalball` is: 

8248 

8249 .. math:: 

8250 

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} 

8255 

8256 where :math:`A = (m / |\beta|)^n \exp(-\beta^2 / 2)`, 

8257 :math:`B = m/|\beta| - |\beta|` and :math:`N` is a normalisation constant. 

8258 

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. 

8263 

8264 References 

8265 ---------- 

8266 .. [1] "Crystal Ball Function", 

8267 https://en.wikipedia.org/wiki/Crystal_Ball_function 

8268 

8269 %(after_notes)s 

8270 

8271 .. versionadded:: 0.19.0 

8272 

8273 %(example)s 

8274 """ 

8275 

8276 def _pdf(self, x, beta, m): 

8277 """ 

8278 Return PDF of the crystalball function. 

8279 

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)) 

8288 

8289 def rhs(x, beta, m): 

8290 return np.exp(-x**2 / 2) 

8291 

8292 def lhs(x, beta, m): 

8293 return ((m/beta)**m * np.exp(-beta**2 / 2.0) * 

8294 (m/beta - beta - x)**(-m)) 

8295 

8296 return N * _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs) 

8297 

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)) 

8304 

8305 def rhs(x, beta, m): 

8306 return -x**2/2 

8307 

8308 def lhs(x, beta, m): 

8309 return m*np.log(m/beta) - beta**2/2 - m*np.log(m/beta - beta - x) 

8310 

8311 return np.log(N) + _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs) 

8312 

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)) 

8319 

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))) 

8323 

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)) 

8327 

8328 return N * _lazywhere(x > -beta, (x, beta, m), f=rhs, f2=lhs) 

8329 

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) 

8334 

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))) 

8341 

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)) 

8347 

8348 return _lazywhere(p < pbeta, (p, beta, m), f=ppf_less, f2=ppf_greater) 

8349 

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)) 

8356 

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 

8371 

8372 return N * _lazywhere(n + 1 < m, (n, beta, m), 

8373 np.vectorize(n_th_moment, otypes=[np.float]), 

8374 np.inf) 

8375 

8376 def _argcheck(self, beta, m): 

8377 """ 

8378 Shape parameter bounds are m > 1 and beta > 0. 

8379 """ 

8380 return (m > 1) & (beta > 0) 

8381 

8382 

8383crystalball = crystalball_gen(name='crystalball', longname="A Crystalball Function") 

8384 

8385 

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 

8392 

8393 

8394class argus_gen(rv_continuous): 

8395 r""" 

8396 Argus distribution 

8397 

8398 %(before_notes)s 

8399 

8400 Notes 

8401 ----- 

8402 The probability density function for `argus` is: 

8403 

8404 .. math:: 

8405 

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) 

8408 

8409 for :math:`0 < x < 1` and :math:`\chi > 0`, where 

8410 

8411 .. math:: 

8412 

8413 \Psi(\chi) = \Phi(\chi) - \chi \phi(\chi) - 1/2 

8414 

8415 with :math:`\Phi` and :math:`\phi` being the CDF and PDF of a standard 

8416 normal distribution, respectively. 

8417 

8418 `argus` takes :math:`\chi` as shape a parameter. 

8419 

8420 References 

8421 ---------- 

8422 

8423 .. [1] "ARGUS distribution", 

8424 https://en.wikipedia.org/wiki/ARGUS_distribution 

8425 

8426 %(after_notes)s 

8427 

8428 .. versionadded:: 0.19.0 

8429 

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) 

8436 

8437 def _cdf(self, x, chi): 

8438 return 1.0 - self._sf(x, chi) 

8439 

8440 def _sf(self, x, chi): 

8441 return _argus_phi(chi * np.sqrt(1 - x**2)) / _argus_phi(chi) 

8442 

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() 

8462 

8463 if size == (): 

8464 out = out[()] 

8465 return out 

8466 

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. 

8504 

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 

8525 

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) 

8532 

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) 

8538 

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 

8545 

8546 

8547argus = argus_gen(name='argus', longname="An Argus Function", a=0.0, b=1.0) 

8548 

8549 

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. 

8555 

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. 

8560 

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 

8568 

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. 

8574 

8575 .. versionadded:: 0.19.0 

8576 

8577 Examples 

8578 -------- 

8579 

8580 Create a scipy.stats distribution from a numpy histogram 

8581 

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) 

8587 

8588 Behaves like an ordinary scipy rv_continuous distribution 

8589 

8590 >>> hist_dist.pdf(1.0) 

8591 0.20538577847618705 

8592 >>> hist_dist.cdf(2.0) 

8593 0.90818568543056499 

8594 

8595 PDF is zero above (below) the highest (lowest) bin of the histogram, 

8596 defined by the max (min) of the original dataset 

8597 

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 

8606 

8607 PDF and CDF follow the histogram 

8608 

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() 

8616 

8617 """ 

8618 _support_mask = rv_continuous._support_mask 

8619 

8620 def __init__(self, histogram, *args, **kwargs): 

8621 """ 

8622 Create a new distribution using the given histogram 

8623 

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) 

8650 

8651 def _pdf(self, x): 

8652 """ 

8653 PDF of the histogram 

8654 """ 

8655 return self._hpdf[np.searchsorted(self._hbins, x, side='right')] 

8656 

8657 def _cdf(self, x): 

8658 """ 

8659 CDF calculated from the histogram 

8660 """ 

8661 return np.interp(x, self._hbins, self._hcdf) 

8662 

8663 def _ppf(self, x): 

8664 """ 

8665 Percentile function calculated from the histogram 

8666 """ 

8667 return np.interp(x, self._hcdf, self._hbins) 

8668 

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) 

8673 

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) 

8681 

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 

8689 

8690 

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) 

8694 

8695__all__ = _distn_names + _distn_gen_names + ['rv_histogram']