test_continuous_basic.py 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. import sys
  2. import warnings
  3. import numpy as np
  4. import numpy.testing as npt
  5. import pytest
  6. from pytest import raises as assert_raises
  7. from scipy.integrate import IntegrationWarning
  8. import itertools
  9. from scipy import stats
  10. from .common_tests import (check_normalization, check_moment,
  11. check_mean_expect,
  12. check_var_expect, check_skew_expect,
  13. check_kurt_expect, check_entropy,
  14. check_private_entropy, check_entropy_vect_scale,
  15. check_edge_support, check_named_args,
  16. check_random_state_property,
  17. check_meth_dtype, check_ppf_dtype,
  18. check_cmplx_deriv,
  19. check_pickling, check_rvs_broadcast,
  20. check_freezing, check_munp_expect,)
  21. from scipy.stats._distr_params import distcont
  22. from scipy.stats._distn_infrastructure import rv_continuous_frozen
  23. """
  24. Test all continuous distributions.
  25. Parameters were chosen for those distributions that pass the
  26. Kolmogorov-Smirnov test. This provides safe parameters for each
  27. distributions so that we can perform further testing of class methods.
  28. These tests currently check only/mostly for serious errors and exceptions,
  29. not for numerically exact results.
  30. """
  31. # Note that you need to add new distributions you want tested
  32. # to _distr_params
  33. DECIMAL = 5 # specify the precision of the tests # increased from 0 to 5
  34. _IS_32BIT = (sys.maxsize < 2**32)
  35. # Sets of tests to skip.
  36. # Entries sorted by speed (very slow to slow).
  37. # xslow took > 1s; slow took > 0.5s
  38. xslow_test_cont_basic = {'studentized_range', 'kstwo', 'ksone', 'vonmises', 'kappa4',
  39. 'recipinvgauss', 'vonmises_line', 'gausshyper',
  40. 'rel_breitwigner', 'norminvgauss'}
  41. slow_test_cont_basic = {'crystalball', 'powerlognorm', 'pearson3'}
  42. # test_moments is already marked slow
  43. xslow_test_moments = {'studentized_range', 'ksone', 'vonmises', 'vonmises_line',
  44. 'recipinvgauss', 'kstwo', 'kappa4'}
  45. slow_fit_mle = {'exponweib', 'genexpon', 'genhyperbolic', 'johnsonsb',
  46. 'kappa4', 'powerlognorm', 'tukeylambda'}
  47. xslow_fit_mle = {'gausshyper', 'ncf', 'ncx2', 'recipinvgauss', 'vonmises_line'}
  48. xfail_fit_mle = {'ksone', 'kstwo', 'truncpareto', 'irwinhall'}
  49. skip_fit_mle = {'levy_stable', 'studentized_range'} # far too slow (>10min)
  50. slow_fit_mm = {'chi2', 'expon', 'lognorm', 'loguniform', 'powerlaw', 'reciprocal'}
  51. xslow_fit_mm = {'argus', 'beta', 'exponpow', 'gausshyper', 'gengamma',
  52. 'genhalflogistic', 'geninvgauss', 'gompertz', 'halfgennorm',
  53. 'johnsonsb', 'kstwobign', 'ncx2', 'norminvgauss', 'trapezoid',
  54. 'truncnorm', 'truncweibull_min', 'wrapcauchy'}
  55. xfail_fit_mm = {'alpha', 'betaprime', 'bradford', 'burr', 'burr12', 'cauchy',
  56. 'crystalball', 'dpareto_lognorm', 'exponweib', 'f', 'fisk',
  57. 'foldcauchy', 'genextreme', 'genpareto', 'halfcauchy', 'invgamma',
  58. 'irwinhall', 'jf_skew_t', 'johnsonsu', 'kappa3', 'kappa4', 'landau',
  59. 'levy', 'levy_l', 'loglaplace', 'lomax', 'mielke', 'ncf', 'nct',
  60. 'pareto', 'powerlognorm', 'powernorm', 'rel_breitwigner',
  61. 'skewcauchy', 't', 'truncexpon', 'truncpareto',
  62. 'tukeylambda', 'vonmises', 'vonmises_line'}
  63. skip_fit_mm = {'genexpon', 'genhyperbolic', 'ksone', 'kstwo', 'levy_stable',
  64. 'recipinvgauss', 'studentized_range'} # far too slow (>10min)
  65. # These distributions fail the complex derivative test below.
  66. # Here 'fail' mean produce wrong results and/or raise exceptions, depending
  67. # on the implementation details of corresponding special functions.
  68. # cf https://github.com/scipy/scipy/pull/4979 for a discussion.
  69. fails_cmplx = {'argus', 'beta', 'betaprime', 'cauchy', 'chi', 'chi2', 'cosine',
  70. 'dgamma', 'dpareto_lognorm', 'dweibull', 'erlang', 'f', 'foldcauchy',
  71. 'gamma', 'gausshyper', 'gengamma', 'genhyperbolic',
  72. 'geninvgauss', 'gennorm', 'genpareto',
  73. 'halfcauchy', 'halfgennorm', 'invgamma', 'irwinhall', 'jf_skew_t',
  74. 'ksone', 'kstwo', 'kstwobign', 'landau', 'levy_l', 'loggamma',
  75. 'logistic', 'loguniform', 'maxwell', 'nakagami',
  76. 'ncf', 'nct', 'ncx2', 'norminvgauss', 'pearson3',
  77. 'powerlaw', 'rdist', 'reciprocal', 'rice',
  78. 'skewnorm', 't', 'truncpareto', 'truncweibull_min',
  79. 'tukeylambda', 'vonmises', 'vonmises_line',
  80. 'rv_histogram_instance', 'truncnorm', 'studentized_range',
  81. 'johnsonsb', 'halflogistic', 'rel_breitwigner'}
  82. # Slow test_method_with_lists
  83. slow_with_lists = {'studentized_range'}
  84. # rv_histogram instances, with uniform and non-uniform bins;
  85. # stored as (dist, arg) tuples for cases_test_cont_basic
  86. # and cases_test_moments.
  87. histogram_test_instances = []
  88. case1 = {'a': [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
  89. 6, 6, 6, 7, 7, 7, 8, 8, 9], 'bins': 8} # equal width bins
  90. case2 = {'a': [1, 1], 'bins': [0, 1, 10]} # unequal width bins
  91. for case, density in itertools.product([case1, case2], [True, False]):
  92. _hist = np.histogram(**case, density=density)
  93. _rv_hist = stats.rv_histogram(_hist, density=density)
  94. histogram_test_instances.append((_rv_hist, tuple()))
  95. def cases_test_cont_basic():
  96. for distname, arg in distcont[:] + histogram_test_instances:
  97. if distname == 'levy_stable': # fails; tested separately
  98. continue
  99. if distname in slow_test_cont_basic:
  100. yield pytest.param(distname, arg, marks=pytest.mark.slow)
  101. elif distname in xslow_test_cont_basic:
  102. yield pytest.param(distname, arg, marks=pytest.mark.xslow)
  103. else:
  104. yield distname, arg
  105. @pytest.mark.parametrize('distname,arg', cases_test_cont_basic())
  106. @pytest.mark.parametrize('sn', [500])
  107. def test_cont_basic(distname, arg, sn, num_parallel_threads):
  108. try:
  109. distfn = getattr(stats, distname)
  110. except TypeError:
  111. distfn = distname
  112. distname = 'rv_histogram_instance'
  113. rng = np.random.default_rng(7654565)
  114. rvs = distfn.rvs(size=sn, *arg, random_state=rng)
  115. m, v = distfn.stats(*arg)
  116. if distname not in {'laplace_asymmetric'}:
  117. # TODO: multiple checks in this function are not robust, tweaking the
  118. # seed above will make different distributions fail.
  119. check_sample_meanvar_(m, v, rvs, rng)
  120. check_cdf_ppf(distfn, arg, distname)
  121. check_sf_isf(distfn, arg, distname)
  122. check_cdf_sf(distfn, arg, distname)
  123. check_ppf_isf(distfn, arg, distname)
  124. check_pdf(distfn, arg, distname)
  125. check_pdf_logpdf(distfn, arg, distname)
  126. check_pdf_logpdf_at_endpoints(distfn, arg, distname)
  127. check_cdf_logcdf(distfn, arg, distname)
  128. check_sf_logsf(distfn, arg, distname)
  129. check_ppf_broadcast(distfn, arg, distname)
  130. alpha = 0.01
  131. if distname == 'rv_histogram_instance':
  132. check_distribution_rvs(distfn.cdf, arg, alpha, rvs)
  133. elif distname != 'geninvgauss':
  134. # skip kstest for geninvgauss since cdf is too slow; see test for
  135. # rv generation in TestGenInvGauss in test_distributions.py
  136. check_distribution_rvs(distname, arg, alpha, rvs)
  137. locscale_defaults = (0, 1)
  138. meths = [distfn.pdf, distfn.logpdf, distfn.cdf, distfn.logcdf,
  139. distfn.logsf]
  140. # make sure arguments are within support
  141. spec_x = {'weibull_max': -0.5, 'levy_l': -0.5,
  142. 'pareto': 1.5, 'truncpareto': 3.2, 'tukeylambda': 0.3,
  143. 'rv_histogram_instance': 5.0}
  144. x = spec_x.get(distname, 0.5)
  145. if distname == 'invweibull':
  146. arg = (1,)
  147. elif distname == 'ksone':
  148. arg = (3,)
  149. check_named_args(distfn, x, arg, locscale_defaults, meths)
  150. if num_parallel_threads == 1:
  151. check_random_state_property(distfn, arg)
  152. if distname in ['rel_breitwigner'] and _IS_32BIT:
  153. # gh18414
  154. pytest.skip("fails on Linux 32-bit")
  155. else:
  156. check_pickling(distfn, arg)
  157. check_freezing(distfn, arg)
  158. # Entropy
  159. if distname not in ['kstwobign', 'kstwo', 'ncf']:
  160. check_entropy(distfn, arg, distname)
  161. if distfn.numargs == 0:
  162. check_vecentropy(distfn, arg)
  163. if (distfn.__class__._entropy != stats.rv_continuous._entropy
  164. and distname != 'vonmises'):
  165. check_private_entropy(distfn, arg, stats.rv_continuous)
  166. with warnings.catch_warnings():
  167. warnings.filterwarnings(
  168. "ignore", "The occurrence of roundoff error", IntegrationWarning)
  169. warnings.filterwarnings("ignore", "Extremely bad integrand", IntegrationWarning)
  170. warnings.filterwarnings("ignore", "invalid value", RuntimeWarning)
  171. check_entropy_vect_scale(distfn, arg)
  172. check_retrieving_support(distfn, arg)
  173. check_edge_support(distfn, arg)
  174. check_meth_dtype(distfn, arg, meths)
  175. check_ppf_dtype(distfn, arg)
  176. if distname not in fails_cmplx:
  177. check_cmplx_deriv(distfn, arg)
  178. if distname != 'truncnorm':
  179. check_ppf_private(distfn, arg, distname)
  180. def cases_test_cont_basic_fit():
  181. slow = pytest.mark.slow
  182. xslow = pytest.mark.xslow
  183. fail = pytest.mark.skip(reason="Test fails and may be slow.")
  184. skip = pytest.mark.skip(reason="Test too slow to run to completion (>10m).")
  185. for distname, arg in distcont[:] + histogram_test_instances:
  186. for method in ["MLE", "MM"]:
  187. for fix_args in [True, False]:
  188. if method == 'MLE' and distname in slow_fit_mle:
  189. yield pytest.param(distname, arg, method, fix_args, marks=slow)
  190. continue
  191. if method == 'MLE' and distname in xslow_fit_mle:
  192. yield pytest.param(distname, arg, method, fix_args, marks=xslow)
  193. continue
  194. if method == 'MLE' and distname in xfail_fit_mle:
  195. yield pytest.param(distname, arg, method, fix_args, marks=fail)
  196. continue
  197. if method == 'MLE' and distname in skip_fit_mle:
  198. yield pytest.param(distname, arg, method, fix_args, marks=skip)
  199. continue
  200. if method == 'MM' and distname in slow_fit_mm:
  201. yield pytest.param(distname, arg, method, fix_args, marks=slow)
  202. continue
  203. if method == 'MM' and distname in xslow_fit_mm:
  204. yield pytest.param(distname, arg, method, fix_args, marks=xslow)
  205. continue
  206. if method == 'MM' and distname in xfail_fit_mm:
  207. yield pytest.param(distname, arg, method, fix_args, marks=fail)
  208. continue
  209. if method == 'MM' and distname in skip_fit_mm:
  210. yield pytest.param(distname, arg, method, fix_args, marks=skip)
  211. continue
  212. yield distname, arg, method, fix_args
  213. def test_cont_basic_fit_cases():
  214. # Distribution names should not be in multiple MLE or MM sets
  215. assert (len(xslow_fit_mle.union(xfail_fit_mle).union(skip_fit_mle)) ==
  216. len(xslow_fit_mle) + len(xfail_fit_mle) + len(skip_fit_mle))
  217. assert (len(xslow_fit_mm.union(xfail_fit_mm).union(skip_fit_mm)) ==
  218. len(xslow_fit_mm) + len(xfail_fit_mm) + len(skip_fit_mm))
  219. @pytest.mark.parametrize('distname, arg, method, fix_args',
  220. cases_test_cont_basic_fit())
  221. @pytest.mark.parametrize('n_fit_samples', [200])
  222. def test_cont_basic_fit(distname, arg, n_fit_samples, method, fix_args):
  223. try:
  224. distfn = getattr(stats, distname)
  225. except TypeError:
  226. distfn = distname
  227. rng = np.random.RandomState(765456)
  228. rvs = distfn.rvs(size=n_fit_samples, *arg, random_state=rng)
  229. if fix_args:
  230. check_fit_args_fix(distfn, arg, rvs, method)
  231. else:
  232. check_fit_args(distfn, arg, rvs, method)
  233. @pytest.mark.parametrize('distname,arg', cases_test_cont_basic())
  234. def test_rvs_scalar(distname, arg):
  235. # rvs should return a scalar when given scalar arguments (gh-12428)
  236. try:
  237. distfn = getattr(stats, distname)
  238. except TypeError:
  239. distfn = distname
  240. distname = 'rv_histogram_instance'
  241. assert np.isscalar(distfn.rvs(*arg))
  242. assert np.isscalar(distfn.rvs(*arg, size=()))
  243. assert np.isscalar(distfn.rvs(*arg, size=None))
  244. @pytest.mark.thread_unsafe(reason="global rng")
  245. def test_levy_stable_random_state_property():
  246. # levy_stable only implements rvs(), so it is skipped in the
  247. # main loop in test_cont_basic(). Here we apply just the test
  248. # check_random_state_property to levy_stable.
  249. check_random_state_property(stats.levy_stable, (0.5, 0.1))
  250. def cases_test_moments():
  251. fail_normalization = set()
  252. fail_higher = {'ncf'}
  253. fail_moment = {'johnsonsu'} # generic `munp` is inaccurate for johnsonsu
  254. for distname, arg in distcont[:] + histogram_test_instances:
  255. if distname == 'levy_stable':
  256. continue
  257. if distname in xslow_test_moments:
  258. yield pytest.param(distname, arg, True, True, True, True,
  259. marks=pytest.mark.xslow(reason="too slow"))
  260. continue
  261. cond1 = distname not in fail_normalization
  262. cond2 = distname not in fail_higher
  263. cond3 = distname not in fail_moment
  264. marks = list()
  265. # Currently unused, `marks` can be used to add a timeout to a test of
  266. # a specific distribution. For example, this shows how a timeout could
  267. # be added for the 'skewnorm' distribution:
  268. #
  269. # marks = list()
  270. # if distname == 'skewnorm':
  271. # marks.append(pytest.mark.timeout(300))
  272. yield pytest.param(distname, arg, cond1, cond2, cond3,
  273. False, marks=marks)
  274. if not cond1 or not cond2 or not cond3:
  275. # Run the distributions that have issues twice, once skipping the
  276. # not_ok parts, once with the not_ok parts but marked as knownfail
  277. yield pytest.param(distname, arg, True, True, True, True,
  278. marks=[pytest.mark.xfail] + marks)
  279. @pytest.mark.slow
  280. @pytest.mark.parametrize('distname,arg,normalization_ok,higher_ok,moment_ok,'
  281. 'is_xfailing',
  282. cases_test_moments())
  283. def test_moments(distname, arg, normalization_ok, higher_ok, moment_ok,
  284. is_xfailing):
  285. try:
  286. distfn = getattr(stats, distname)
  287. except TypeError:
  288. distfn = distname
  289. distname = 'rv_histogram_instance'
  290. with warnings.catch_warnings():
  291. warnings.filterwarnings(
  292. "ignore",
  293. "The integral is probably divergent, or slowly convergent.",
  294. IntegrationWarning,
  295. )
  296. warnings.filterwarnings(
  297. "ignore",
  298. "The maximum number of subdivisions.",
  299. IntegrationWarning
  300. )
  301. warnings.filterwarnings(
  302. "ignore",
  303. "The algorithm does not converge.",
  304. IntegrationWarning
  305. )
  306. if is_xfailing:
  307. warnings.simplefilter("ignore", IntegrationWarning)
  308. m, v, s, k = distfn.stats(*arg, moments='mvsk')
  309. with np.errstate(all="ignore"):
  310. if normalization_ok:
  311. check_normalization(distfn, arg, distname)
  312. if higher_ok:
  313. check_mean_expect(distfn, arg, m, distname)
  314. check_skew_expect(distfn, arg, m, v, s, distname)
  315. check_var_expect(distfn, arg, m, v, distname)
  316. check_kurt_expect(distfn, arg, m, v, k, distname)
  317. check_munp_expect(distfn, arg, distname)
  318. check_loc_scale(distfn, arg, m, v, distname)
  319. if moment_ok:
  320. check_moment(distfn, arg, m, v, distname)
  321. @pytest.mark.parametrize('dist,shape_args', distcont)
  322. def test_rvs_broadcast(dist, shape_args):
  323. if dist in ['gausshyper', 'studentized_range']:
  324. pytest.skip("too slow")
  325. if dist in ['rel_breitwigner'] and _IS_32BIT:
  326. # gh18414
  327. pytest.skip("fails on Linux 32-bit")
  328. # If shape_only is True, it means the _rvs method of the
  329. # distribution uses more than one random number to generate a random
  330. # variate. That means the result of using rvs with broadcasting or
  331. # with a nontrivial size will not necessarily be the same as using the
  332. # numpy.vectorize'd version of rvs(), so we can only compare the shapes
  333. # of the results, not the values.
  334. # Whether or not a distribution is in the following list is an
  335. # implementation detail of the distribution, not a requirement. If
  336. # the implementation the rvs() method of a distribution changes, this
  337. # test might also have to be changed.
  338. shape_only = dist in ['argus', 'betaprime', 'dgamma', 'dpareto_lognorm', 'dweibull',
  339. 'exponnorm', 'genhyperbolic', 'geninvgauss', 'landau',
  340. 'levy_stable', 'nct', 'norminvgauss', 'rice',
  341. 'skewnorm', 'semicircular', 'gennorm', 'loggamma']
  342. distfunc = getattr(stats, dist)
  343. loc = np.zeros(2)
  344. scale = np.ones((3, 1))
  345. nargs = distfunc.numargs
  346. allargs = []
  347. bshape = [3, 2]
  348. # Generate shape parameter arguments...
  349. for k in range(nargs):
  350. shp = (k + 4,) + (1,)*(k + 2)
  351. allargs.append(shape_args[k]*np.ones(shp))
  352. bshape.insert(0, k + 4)
  353. allargs.extend([loc, scale])
  354. # bshape holds the expected shape when loc, scale, and the shape
  355. # parameters are all broadcast together.
  356. check_rvs_broadcast(distfunc, dist, allargs, bshape, shape_only, 'd')
  357. # Expected values of the SF, CDF, PDF were computed using
  358. # mpmath with mpmath.mp.dps = 50 and output at 20:
  359. #
  360. # def ks(x, n):
  361. # x = mpmath.mpf(x)
  362. # logp = -mpmath.power(6.0*n*x+1.0, 2)/18.0/n
  363. # sf, cdf = mpmath.exp(logp), -mpmath.expm1(logp)
  364. # pdf = (6.0*n*x+1.0) * 2 * sf/3
  365. # print(mpmath.nstr(sf, 20), mpmath.nstr(cdf, 20), mpmath.nstr(pdf, 20))
  366. #
  367. # Tests use 1/n < x < 1-1/n and n > 1e6 to use the asymptotic computation.
  368. # Larger x has a smaller sf.
  369. @pytest.mark.parametrize('x,n,sf,cdf,pdf,rtol',
  370. [(2.0e-5, 1000000000,
  371. 0.44932297307934442379, 0.55067702692065557621,
  372. 35946.137394996276407, 5e-15),
  373. (2.0e-9, 1000000000,
  374. 0.99999999061111115519, 9.3888888448132728224e-9,
  375. 8.6666665852962971765, 5e-14),
  376. (5.0e-4, 1000000000,
  377. 7.1222019433090374624e-218, 1.0,
  378. 1.4244408634752704094e-211, 5e-14)])
  379. def test_gh17775_regression(x, n, sf, cdf, pdf, rtol):
  380. # Regression test for gh-17775. In scipy 1.9.3 and earlier,
  381. # these test would fail.
  382. #
  383. # KS one asymptotic sf ~ e^(-(6nx+1)^2 / 18n)
  384. # Given a large 32-bit integer n, 6n will overflow in the c implementation.
  385. # Example of broken behaviour:
  386. # ksone.sf(2.0e-5, 1000000000) == 0.9374359693473666
  387. ks = stats.ksone
  388. vals = np.array([ks.sf(x, n), ks.cdf(x, n), ks.pdf(x, n)])
  389. expected = np.array([sf, cdf, pdf])
  390. npt.assert_allclose(vals, expected, rtol=rtol)
  391. # The sf+cdf must sum to 1.0.
  392. npt.assert_equal(vals[0] + vals[1], 1.0)
  393. # Check inverting the (potentially very small) sf (uses a lower tolerance)
  394. npt.assert_allclose([ks.isf(sf, n)], [x], rtol=1e-8)
  395. def test_rvs_gh2069_regression():
  396. # Regression tests for gh-2069. In scipy 0.17 and earlier,
  397. # these tests would fail.
  398. #
  399. # A typical example of the broken behavior:
  400. # >>> norm.rvs(loc=np.zeros(5), scale=np.ones(5))
  401. # array([-2.49613705, -2.49613705, -2.49613705, -2.49613705, -2.49613705])
  402. rng = np.random.RandomState(123)
  403. vals = stats.norm.rvs(loc=np.zeros(5), scale=1, random_state=rng)
  404. d = np.diff(vals)
  405. npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!")
  406. vals = stats.norm.rvs(loc=0, scale=np.ones(5), random_state=rng)
  407. d = np.diff(vals)
  408. npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!")
  409. vals = stats.norm.rvs(loc=np.zeros(5), scale=np.ones(5), random_state=rng)
  410. d = np.diff(vals)
  411. npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!")
  412. vals = stats.norm.rvs(loc=np.array([[0], [0]]), scale=np.ones(5),
  413. random_state=rng)
  414. d = np.diff(vals.ravel())
  415. npt.assert_(np.all(d != 0), "All the values are equal, but they shouldn't be!")
  416. assert_raises(ValueError, stats.norm.rvs, [[0, 0], [0, 0]],
  417. [[1, 1], [1, 1]], 1)
  418. assert_raises(ValueError, stats.gamma.rvs, [2, 3, 4, 5], 0, 1, (2, 2))
  419. assert_raises(ValueError, stats.gamma.rvs, [1, 1, 1, 1], [0, 0, 0, 0],
  420. [[1], [2]], (4,))
  421. def test_nomodify_gh9900_regression():
  422. # Regression test for gh-9990
  423. # Prior to gh-9990, calls to stats.truncnorm._cdf() use what ever was
  424. # set inside the stats.truncnorm instance during stats.truncnorm.cdf().
  425. # This could cause issues with multi-threaded code.
  426. # Since then, the calls to cdf() are not permitted to modify the global
  427. # stats.truncnorm instance.
  428. tn = stats.truncnorm
  429. # Use the right-half truncated normal
  430. # Check that the cdf and _cdf return the same result.
  431. npt.assert_almost_equal(tn.cdf(1, 0, np.inf),
  432. 0.6826894921370859)
  433. npt.assert_almost_equal(tn._cdf([1], [0], [np.inf]),
  434. 0.6826894921370859)
  435. # Now use the left-half truncated normal
  436. npt.assert_almost_equal(tn.cdf(-1, -np.inf, 0),
  437. 0.31731050786291415)
  438. npt.assert_almost_equal(tn._cdf([-1], [-np.inf], [0]),
  439. 0.31731050786291415)
  440. # Check that the right-half truncated normal _cdf hasn't changed
  441. npt.assert_almost_equal(tn._cdf([1], [0], [np.inf]),
  442. 0.6826894921370859) # Not 1.6826894921370859
  443. npt.assert_almost_equal(tn.cdf(1, 0, np.inf),
  444. 0.6826894921370859)
  445. # Check that the left-half truncated normal _cdf hasn't changed
  446. npt.assert_almost_equal(tn._cdf([-1], [-np.inf], [0]),
  447. 0.31731050786291415) # Not -0.6826894921370859
  448. npt.assert_almost_equal(tn.cdf(1, -np.inf, 0),
  449. 1) # Not 1.6826894921370859
  450. npt.assert_almost_equal(tn.cdf(-1, -np.inf, 0),
  451. 0.31731050786291415) # Not -0.6826894921370859
  452. def test_broadcast_gh9990_regression():
  453. # Regression test for gh-9990
  454. # The x-value 7 only lies within the support of 4 of the supplied
  455. # distributions. Prior to 9990, one array passed to
  456. # stats.reciprocal._cdf would have 4 elements, but an array
  457. # previously stored by stats.reciprocal_argcheck() would have 6, leading
  458. # to a broadcast error.
  459. a = np.array([1, 2, 3, 4, 5, 6])
  460. b = np.array([8, 16, 1, 32, 1, 48])
  461. ans = [stats.reciprocal.cdf(7, _a, _b) for _a, _b in zip(a,b)]
  462. npt.assert_array_almost_equal(stats.reciprocal.cdf(7, a, b), ans)
  463. ans = [stats.reciprocal.cdf(1, _a, _b) for _a, _b in zip(a,b)]
  464. npt.assert_array_almost_equal(stats.reciprocal.cdf(1, a, b), ans)
  465. ans = [stats.reciprocal.cdf(_a, _a, _b) for _a, _b in zip(a,b)]
  466. npt.assert_array_almost_equal(stats.reciprocal.cdf(a, a, b), ans)
  467. ans = [stats.reciprocal.cdf(_b, _a, _b) for _a, _b in zip(a,b)]
  468. npt.assert_array_almost_equal(stats.reciprocal.cdf(b, a, b), ans)
  469. def test_broadcast_gh7933_regression():
  470. # Check broadcast works
  471. stats.truncnorm.logpdf(
  472. np.array([3.0, 2.0, 1.0]),
  473. a=(1.5 - np.array([6.0, 5.0, 4.0])) / 3.0,
  474. b=np.inf,
  475. loc=np.array([6.0, 5.0, 4.0]),
  476. scale=3.0
  477. )
  478. def test_gh2002_regression():
  479. # Add a check that broadcast works in situations where only some
  480. # x-values are compatible with some of the shape arguments.
  481. x = np.r_[-2:2:101j]
  482. a = np.r_[-np.ones(50), np.ones(51)]
  483. expected = [stats.truncnorm.pdf(_x, _a, np.inf) for _x, _a in zip(x, a)]
  484. ans = stats.truncnorm.pdf(x, a, np.inf)
  485. npt.assert_array_almost_equal(ans, expected)
  486. def test_gh1320_regression():
  487. # Check that the first example from gh-1320 now works.
  488. c = 2.62
  489. stats.genextreme.ppf(0.5, np.array([[c], [c + 0.5]]))
  490. # The other examples in gh-1320 appear to have stopped working
  491. # some time ago.
  492. # ans = stats.genextreme.moment(2, np.array([c, c + 0.5]))
  493. # expected = np.array([25.50105963, 115.11191437])
  494. # stats.genextreme.moment(5, np.array([[c], [c + 0.5]]))
  495. # stats.genextreme.moment(5, np.array([c, c + 0.5]))
  496. def test_method_of_moments():
  497. # example from https://en.wikipedia.org/wiki/Method_of_moments_(statistics)
  498. x = [0, 0, 0, 0, 1]
  499. a = 1/5 - 2*np.sqrt(3)/5
  500. b = 1/5 + 2*np.sqrt(3)/5
  501. # force use of method of moments (uniform.fit is overridden)
  502. loc, scale = super(type(stats.uniform), stats.uniform).fit(x, method="MM")
  503. npt.assert_almost_equal(loc, a, decimal=4)
  504. npt.assert_almost_equal(loc+scale, b, decimal=4)
  505. def check_sample_meanvar_(popmean, popvar, sample, rng):
  506. if np.isfinite(popmean):
  507. check_sample_mean(sample, popmean)
  508. if np.isfinite(popvar):
  509. check_sample_var(sample, popvar, rng)
  510. def check_sample_mean(sample, popmean):
  511. # Checks for unlikely difference between sample mean and population mean
  512. prob = stats.ttest_1samp(sample, popmean).pvalue
  513. assert prob > 0.01
  514. def check_sample_var(sample, popvar, rng):
  515. # check that population mean lies within the CI bootstrapped from the
  516. # sample. This used to be a chi-squared test for variance, but there were
  517. # too many false positives
  518. res = stats.bootstrap(
  519. (sample,),
  520. lambda x, axis: x.var(ddof=1, axis=axis),
  521. confidence_level=0.995,
  522. rng=rng,
  523. )
  524. conf = res.confidence_interval
  525. low, high = conf.low, conf.high
  526. assert low <= popvar <= high
  527. def check_cdf_ppf(distfn, arg, msg):
  528. values = [0.001, 0.5, 0.999]
  529. npt.assert_almost_equal(distfn.cdf(distfn.ppf(values, *arg), *arg),
  530. values, decimal=DECIMAL, err_msg=msg +
  531. ' - cdf-ppf roundtrip')
  532. def check_sf_isf(distfn, arg, msg):
  533. npt.assert_almost_equal(distfn.sf(distfn.isf([0.1, 0.5, 0.9], *arg), *arg),
  534. [0.1, 0.5, 0.9], decimal=DECIMAL, err_msg=msg +
  535. ' - sf-isf roundtrip')
  536. def check_cdf_sf(distfn, arg, msg):
  537. npt.assert_almost_equal(distfn.cdf([0.1, 0.9], *arg),
  538. 1.0 - distfn.sf([0.1, 0.9], *arg),
  539. decimal=DECIMAL, err_msg=msg +
  540. ' - cdf-sf relationship')
  541. def check_ppf_isf(distfn, arg, msg):
  542. p = np.array([0.1, 0.9])
  543. npt.assert_almost_equal(distfn.isf(p, *arg), distfn.ppf(1-p, *arg),
  544. decimal=DECIMAL, err_msg=msg +
  545. ' - ppf-isf relationship')
  546. def check_pdf(distfn, arg, msg):
  547. # compares pdf at median with numerical derivative of cdf
  548. median = distfn.ppf(0.5, *arg)
  549. eps = 1e-6
  550. pdfv = distfn.pdf(median, *arg)
  551. if (pdfv < 1e-4) or (pdfv > 1e4):
  552. # avoid checking a case where pdf is close to zero or
  553. # huge (singularity)
  554. median = median + 0.1
  555. pdfv = distfn.pdf(median, *arg)
  556. cdfdiff = (distfn.cdf(median + eps, *arg) -
  557. distfn.cdf(median - eps, *arg))/eps/2.0
  558. # replace with better diff and better test (more points),
  559. # actually, this works pretty well
  560. msg += ' - cdf-pdf relationship'
  561. npt.assert_almost_equal(pdfv, cdfdiff, decimal=DECIMAL, err_msg=msg)
  562. def check_pdf_logpdf(distfn, args, msg):
  563. # compares pdf at several points with the log of the pdf
  564. points = np.array([0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
  565. vals = distfn.ppf(points, *args)
  566. vals = vals[np.isfinite(vals)]
  567. pdf = distfn.pdf(vals, *args)
  568. logpdf = distfn.logpdf(vals, *args)
  569. pdf = pdf[(pdf != 0) & np.isfinite(pdf)]
  570. logpdf = logpdf[np.isfinite(logpdf)]
  571. msg += " - logpdf-log(pdf) relationship"
  572. npt.assert_almost_equal(np.log(pdf), logpdf, decimal=7, err_msg=msg)
  573. def check_pdf_logpdf_at_endpoints(distfn, args, msg):
  574. # compares pdf with the log of the pdf at the (finite) end points
  575. points = np.array([0, 1])
  576. vals = distfn.ppf(points, *args)
  577. vals = vals[np.isfinite(vals)]
  578. pdf = distfn.pdf(vals, *args)
  579. logpdf = distfn.logpdf(vals, *args)
  580. pdf = pdf[(pdf != 0) & np.isfinite(pdf)]
  581. logpdf = logpdf[np.isfinite(logpdf)]
  582. msg += " - logpdf-log(pdf) relationship"
  583. npt.assert_almost_equal(np.log(pdf), logpdf, decimal=7, err_msg=msg)
  584. def check_sf_logsf(distfn, args, msg):
  585. # compares sf at several points with the log of the sf
  586. points = np.array([0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0])
  587. vals = distfn.ppf(points, *args)
  588. vals = vals[np.isfinite(vals)]
  589. sf = distfn.sf(vals, *args)
  590. logsf = distfn.logsf(vals, *args)
  591. sf = sf[sf != 0]
  592. logsf = logsf[np.isfinite(logsf)]
  593. msg += " - logsf-log(sf) relationship"
  594. npt.assert_almost_equal(np.log(sf), logsf, decimal=7, err_msg=msg)
  595. def check_cdf_logcdf(distfn, args, msg):
  596. # compares cdf at several points with the log of the cdf
  597. points = np.array([0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0])
  598. vals = distfn.ppf(points, *args)
  599. vals = vals[np.isfinite(vals)]
  600. cdf = distfn.cdf(vals, *args)
  601. logcdf = distfn.logcdf(vals, *args)
  602. cdf = cdf[cdf != 0]
  603. logcdf = logcdf[np.isfinite(logcdf)]
  604. msg += " - logcdf-log(cdf) relationship"
  605. npt.assert_almost_equal(np.log(cdf), logcdf, decimal=7, err_msg=msg)
  606. def check_ppf_broadcast(distfn, arg, msg):
  607. # compares ppf for multiple argsets.
  608. num_repeats = 5
  609. args = [] * num_repeats
  610. if arg:
  611. args = [np.array([_] * num_repeats) for _ in arg]
  612. median = distfn.ppf(0.5, *arg)
  613. medians = distfn.ppf(0.5, *args)
  614. msg += " - ppf multiple"
  615. npt.assert_almost_equal(medians, [median] * num_repeats, decimal=7, err_msg=msg)
  616. def check_distribution_rvs(dist, args, alpha, rvs):
  617. # dist is either a cdf function or name of a distribution in scipy.stats.
  618. # args are the args for scipy.stats.dist(*args)
  619. # alpha is a significance level, ~0.01
  620. # rvs is array_like of random variables
  621. # test from scipy.stats.tests
  622. # this version reuses existing random variables
  623. D, pval = stats.kstest(rvs, dist, args=args, N=1000)
  624. if (pval < alpha):
  625. # The rvs passed in failed the K-S test, which _could_ happen
  626. # but is unlikely if alpha is small enough.
  627. # Repeat the test with a new sample of rvs.
  628. # Generate 1000 rvs, perform a K-S test that the new sample of rvs
  629. # are distributed according to the distribution.
  630. D, pval = stats.kstest(dist, dist, args=args, N=1000)
  631. npt.assert_(pval > alpha, "D = " + str(D) + "; pval = " + str(pval) +
  632. "; alpha = " + str(alpha) + "\nargs = " + str(args))
  633. def check_vecentropy(distfn, args):
  634. npt.assert_equal(distfn.vecentropy(*args), distfn._entropy(*args))
  635. def check_loc_scale(distfn, arg, m, v, msg):
  636. # Make `loc` and `scale` arrays to catch bugs like gh-13580 where
  637. # `loc` and `scale` arrays improperly broadcast with shapes.
  638. loc, scale = np.array([10.0, 20.0]), np.array([10.0, 20.0])
  639. mt, vt = distfn.stats(*arg, loc=loc, scale=scale)
  640. npt.assert_allclose(m*scale + loc, mt)
  641. npt.assert_allclose(v*scale*scale, vt)
  642. def check_ppf_private(distfn, arg, msg):
  643. # fails by design for truncnorm self.nb not defined
  644. ppfs = distfn._ppf(np.array([0.1, 0.5, 0.9]), *arg)
  645. npt.assert_(not np.any(np.isnan(ppfs)), msg + 'ppf private is nan')
  646. def check_retrieving_support(distfn, args):
  647. loc, scale = 1, 2
  648. supp = distfn.support(*args)
  649. supp_loc_scale = distfn.support(*args, loc=loc, scale=scale)
  650. npt.assert_almost_equal(np.array(supp)*scale + loc,
  651. np.array(supp_loc_scale))
  652. def check_fit_args(distfn, arg, rvs, method):
  653. with np.errstate(all='ignore'), warnings.catch_warnings():
  654. warnings.filterwarnings("ignore", category=RuntimeWarning,
  655. message="The shape parameter of the erlang")
  656. warnings.filterwarnings("ignore", category=RuntimeWarning,
  657. message="floating point number truncated")
  658. vals = distfn.fit(rvs, method=method)
  659. vals2 = distfn.fit(rvs, optimizer='powell', method=method)
  660. # Only check the length of the return; accuracy tested in test_fit.py
  661. npt.assert_(len(vals) == 2+len(arg))
  662. npt.assert_(len(vals2) == 2+len(arg))
  663. def check_fit_args_fix(distfn, arg, rvs, method):
  664. with np.errstate(all='ignore'), warnings.catch_warnings():
  665. warnings.filterwarnings("ignore", category=RuntimeWarning,
  666. message="The shape parameter of the erlang")
  667. vals = distfn.fit(rvs, floc=0, method=method)
  668. vals2 = distfn.fit(rvs, fscale=1, method=method)
  669. npt.assert_(len(vals) == 2+len(arg))
  670. npt.assert_(vals[-2] == 0)
  671. npt.assert_(vals2[-1] == 1)
  672. npt.assert_(len(vals2) == 2+len(arg))
  673. if len(arg) > 0:
  674. vals3 = distfn.fit(rvs, f0=arg[0], method=method)
  675. npt.assert_(len(vals3) == 2+len(arg))
  676. npt.assert_(vals3[0] == arg[0])
  677. if len(arg) > 1:
  678. vals4 = distfn.fit(rvs, f1=arg[1], method=method)
  679. npt.assert_(len(vals4) == 2+len(arg))
  680. npt.assert_(vals4[1] == arg[1])
  681. if len(arg) > 2:
  682. vals5 = distfn.fit(rvs, f2=arg[2], method=method)
  683. npt.assert_(len(vals5) == 2+len(arg))
  684. npt.assert_(vals5[2] == arg[2])
  685. def cases_test_methods_with_lists():
  686. for distname, arg in distcont:
  687. if distname in slow_with_lists:
  688. yield pytest.param(distname, arg, marks=pytest.mark.slow)
  689. else:
  690. yield distname, arg
  691. @pytest.mark.parametrize('method', ['pdf', 'logpdf', 'cdf', 'logcdf',
  692. 'sf', 'logsf', 'ppf', 'isf'])
  693. @pytest.mark.parametrize('distname, args', cases_test_methods_with_lists())
  694. def test_methods_with_lists(method, distname, args):
  695. # Test that the continuous distributions can accept Python lists
  696. # as arguments.
  697. dist = getattr(stats, distname)
  698. f = getattr(dist, method)
  699. if distname == 'invweibull' and method.startswith('log'):
  700. x = [1.5, 2]
  701. else:
  702. x = [0.1, 0.2]
  703. shape2 = [[a]*2 for a in args]
  704. loc = [0, 0.1]
  705. scale = [1, 1.01]
  706. result = f(x, *shape2, loc=loc, scale=scale)
  707. npt.assert_allclose(result,
  708. [f(*v) for v in zip(x, *shape2, loc, scale)],
  709. rtol=1e-14, atol=5e-14)
  710. def test_burr_fisk_moment_gh13234_regression():
  711. vals0 = stats.burr.moment(1, 5, 4)
  712. assert isinstance(vals0, float)
  713. vals1 = stats.fisk.moment(1, 8)
  714. assert isinstance(vals1, float)
  715. def test_moments_with_array_gh12192_regression():
  716. # array loc and scalar scale
  717. vals0 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]), scale=1)
  718. expected0 = np.array([1., 2., 3.])
  719. npt.assert_equal(vals0, expected0)
  720. # array loc and invalid scalar scale
  721. vals1 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]), scale=-1)
  722. expected1 = np.array([np.nan, np.nan, np.nan])
  723. npt.assert_equal(vals1, expected1)
  724. # array loc and array scale with invalid entries
  725. vals2 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]),
  726. scale=[-3, 1, 0])
  727. expected2 = np.array([np.nan, 2., np.nan])
  728. npt.assert_equal(vals2, expected2)
  729. # (loc == 0) & (scale < 0)
  730. vals3 = stats.norm.moment(order=2, loc=0, scale=-4)
  731. expected3 = np.nan
  732. npt.assert_equal(vals3, expected3)
  733. assert isinstance(vals3, expected3.__class__)
  734. # array loc with 0 entries and scale with invalid entries
  735. vals4 = stats.norm.moment(order=2, loc=[1, 0, 2], scale=[3, -4, -5])
  736. expected4 = np.array([10., np.nan, np.nan])
  737. npt.assert_equal(vals4, expected4)
  738. # all(loc == 0) & (array scale with invalid entries)
  739. vals5 = stats.norm.moment(order=2, loc=[0, 0, 0], scale=[5., -2, 100.])
  740. expected5 = np.array([25., np.nan, 10000.])
  741. npt.assert_equal(vals5, expected5)
  742. # all( (loc == 0) & (scale < 0) )
  743. vals6 = stats.norm.moment(order=2, loc=[0, 0, 0], scale=[-5., -2, -100.])
  744. expected6 = np.array([np.nan, np.nan, np.nan])
  745. npt.assert_equal(vals6, expected6)
  746. # scalar args, loc, and scale
  747. vals7 = stats.chi.moment(order=2, df=1, loc=0, scale=0)
  748. expected7 = np.nan
  749. npt.assert_equal(vals7, expected7)
  750. assert isinstance(vals7, expected7.__class__)
  751. # array args, scalar loc, and scalar scale
  752. vals8 = stats.chi.moment(order=2, df=[1, 2, 3], loc=0, scale=0)
  753. expected8 = np.array([np.nan, np.nan, np.nan])
  754. npt.assert_equal(vals8, expected8)
  755. # array args, array loc, and array scale
  756. vals9 = stats.chi.moment(order=2, df=[1, 2, 3], loc=[1., 0., 2.],
  757. scale=[1., -3., 0.])
  758. expected9 = np.array([3.59576912, np.nan, np.nan])
  759. npt.assert_allclose(vals9, expected9, rtol=1e-8)
  760. # (n > 4), all(loc != 0), and all(scale != 0)
  761. vals10 = stats.norm.moment(5, [1., 2.], [1., 2.])
  762. expected10 = np.array([26., 832.])
  763. npt.assert_allclose(vals10, expected10, rtol=1e-13)
  764. # test broadcasting and more
  765. a = [-1.1, 0, 1, 2.2, np.pi]
  766. b = [-1.1, 0, 1, 2.2, np.pi]
  767. loc = [-1.1, 0, np.sqrt(2)]
  768. scale = [-2.1, 0, 1, 2.2, np.pi]
  769. a = np.array(a).reshape((-1, 1, 1, 1))
  770. b = np.array(b).reshape((-1, 1, 1))
  771. loc = np.array(loc).reshape((-1, 1))
  772. scale = np.array(scale)
  773. vals11 = stats.beta.moment(order=2, a=a, b=b, loc=loc, scale=scale)
  774. a, b, loc, scale = np.broadcast_arrays(a, b, loc, scale)
  775. for i in np.ndenumerate(a):
  776. with np.errstate(invalid='ignore', divide='ignore'):
  777. i = i[0] # just get the index
  778. # check against same function with scalar input
  779. expected = stats.beta.moment(order=2, a=a[i], b=b[i],
  780. loc=loc[i], scale=scale[i])
  781. np.testing.assert_equal(vals11[i], expected)
  782. def test_broadcasting_in_moments_gh12192_regression():
  783. vals0 = stats.norm.moment(order=1, loc=np.array([1, 2, 3]), scale=[[1]])
  784. expected0 = np.array([[1., 2., 3.]])
  785. npt.assert_equal(vals0, expected0)
  786. assert vals0.shape == expected0.shape
  787. vals1 = stats.norm.moment(order=1, loc=np.array([[1], [2], [3]]),
  788. scale=[1, 2, 3])
  789. expected1 = np.array([[1., 1., 1.], [2., 2., 2.], [3., 3., 3.]])
  790. npt.assert_equal(vals1, expected1)
  791. assert vals1.shape == expected1.shape
  792. vals2 = stats.chi.moment(order=1, df=[1., 2., 3.], loc=0., scale=1.)
  793. expected2 = np.array([0.79788456, 1.25331414, 1.59576912])
  794. npt.assert_allclose(vals2, expected2, rtol=1e-8)
  795. assert vals2.shape == expected2.shape
  796. vals3 = stats.chi.moment(order=1, df=[[1.], [2.], [3.]], loc=[0., 1., 2.],
  797. scale=[-1., 0., 3.])
  798. expected3 = np.array([[np.nan, np.nan, 4.39365368],
  799. [np.nan, np.nan, 5.75994241],
  800. [np.nan, np.nan, 6.78730736]])
  801. npt.assert_allclose(vals3, expected3, rtol=1e-8)
  802. assert vals3.shape == expected3.shape
  803. @pytest.mark.slow
  804. def test_kappa3_array_gh13582():
  805. # https://github.com/scipy/scipy/pull/15140#issuecomment-994958241
  806. shapes = [0.5, 1.5, 2.5, 3.5, 4.5]
  807. moments = 'mvsk'
  808. res = np.array([[stats.kappa3.stats(shape, moments=moment)
  809. for shape in shapes] for moment in moments])
  810. res2 = np.array(stats.kappa3.stats(shapes, moments=moments))
  811. npt.assert_allclose(res, res2)
  812. @pytest.mark.xslow
  813. def test_kappa4_array_gh13582():
  814. h = np.array([-0.5, 2.5, 3.5, 4.5, -3])
  815. k = np.array([-0.5, 1, -1.5, 0, 3.5])
  816. moments = 'mvsk'
  817. res = np.array([[stats.kappa4.stats(h[i], k[i], moments=moment)
  818. for i in range(5)] for moment in moments])
  819. res2 = np.array(stats.kappa4.stats(h, k, moments=moments))
  820. npt.assert_allclose(res, res2)
  821. # https://github.com/scipy/scipy/pull/15250#discussion_r775112913
  822. h = np.array([-1, -1/4, -1/4, 1, -1, 0])
  823. k = np.array([1, 1, 1/2, -1/3, -1, 0])
  824. res = np.array([[stats.kappa4.stats(h[i], k[i], moments=moment)
  825. for i in range(6)] for moment in moments])
  826. res2 = np.array(stats.kappa4.stats(h, k, moments=moments))
  827. npt.assert_allclose(res, res2)
  828. # https://github.com/scipy/scipy/pull/15250#discussion_r775115021
  829. h = np.array([-1, -0.5, 1])
  830. k = np.array([-1, -0.5, 0, 1])[:, None]
  831. res2 = np.array(stats.kappa4.stats(h, k, moments=moments))
  832. assert res2.shape == (4, 4, 3)
  833. def test_frozen_attributes(monkeypatch):
  834. # gh-14827 reported that all frozen distributions had both pmf and pdf
  835. # attributes; continuous should have pdf and discrete should have pmf.
  836. message = "'rv_continuous_frozen' object has no attribute"
  837. with pytest.raises(AttributeError, match=message):
  838. stats.norm().pmf
  839. with pytest.raises(AttributeError, match=message):
  840. stats.norm().logpmf
  841. monkeypatch.setattr(stats.norm, "pmf", "herring", raising=False)
  842. frozen_norm = stats.norm()
  843. assert isinstance(frozen_norm, rv_continuous_frozen)
  844. assert not hasattr(frozen_norm, "pmf")
  845. def test_skewnorm_pdf_gh16038():
  846. rng = np.random.default_rng(0)
  847. x, a = -np.inf, 0
  848. npt.assert_equal(stats.skewnorm.pdf(x, a), stats.norm.pdf(x))
  849. x, a = rng.random(size=(3, 3)), rng.random(size=(3, 3))
  850. mask = rng.random(size=(3, 3)) < 0.5
  851. a[mask] = 0
  852. x_norm = x[mask]
  853. res = stats.skewnorm.pdf(x, a)
  854. npt.assert_equal(res[mask], stats.norm.pdf(x_norm))
  855. npt.assert_equal(res[~mask], stats.skewnorm.pdf(x[~mask], a[~mask]))
  856. # for scalar input, these functions should return scalar output
  857. scalar_out = [['rvs', []], ['pdf', [0]], ['logpdf', [0]], ['cdf', [0]],
  858. ['logcdf', [0]], ['sf', [0]], ['logsf', [0]], ['ppf', [0]],
  859. ['isf', [0]], ['moment', [1]], ['entropy', []], ['expect', []],
  860. ['median', []], ['mean', []], ['std', []], ['var', []]]
  861. scalars_out = [['interval', [0.95]], ['support', []], ['stats', ['mv']]]
  862. @pytest.mark.parametrize('case', scalar_out + scalars_out)
  863. def test_scalar_for_scalar(case):
  864. # Some rv_continuous functions returned 0d array instead of NumPy scalar
  865. # Guard against regression
  866. method_name, args = case
  867. method = getattr(stats.norm(), method_name)
  868. res = method(*args)
  869. if case in scalar_out:
  870. assert isinstance(res, np.number)
  871. else:
  872. assert isinstance(res[0], np.number)
  873. assert isinstance(res[1], np.number)
  874. def test_scalar_for_scalar2():
  875. # test methods that are not attributes of frozen distributions
  876. res = stats.norm.fit([1, 2, 3])
  877. assert isinstance(res[0], np.number)
  878. assert isinstance(res[1], np.number)
  879. res = stats.norm.fit_loc_scale([1, 2, 3])
  880. assert isinstance(res[0], np.number)
  881. assert isinstance(res[1], np.number)
  882. res = stats.norm.nnlf((0, 1), [1, 2, 3])
  883. assert isinstance(res, np.number)