From ed9760e60a28b8f13e5644494033e2dab9aafbcd Mon Sep 17 00:00:00 2001 From: Matt Haberland Date: Mon, 3 Oct 2022 12:39:02 -0700 Subject: [PATCH] MAINT: stats.pearson3: fix ppf for negative skew (#17055) --- scipy/stats/_continuous_distns.py | 4 +++- scipy/stats/_distr_params.py | 1 + scipy/stats/tests/test_distributions.py | 19 +++++++++++++++++++ scipy/stats/tests/test_fit.py | 4 ++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/scipy/stats/_continuous_distns.py b/scipy/stats/_continuous_distns.py index 48645ba11dbc..5bc098aded66 100644 --- a/scipy/stats/_continuous_distns.py +++ b/scipy/stats/_continuous_distns.py @@ -6910,7 +6910,9 @@ def _ppf(self, q, skew): ans, q, _, mask, invmask, beta, alpha, zeta = ( self._preprocess(q, skew)) ans[mask] = _norm_ppf(q[mask]) - ans[invmask] = sc.gammaincinv(alpha, q[invmask])/beta + zeta + q = q[invmask] + q[beta < 0] = 1 - q[beta < 0] # for negative skew; see gh-17050 + ans[invmask] = sc.gammaincinv(alpha, q)/beta + zeta return ans @_call_super_mom diff --git a/scipy/stats/_distr_params.py b/scipy/stats/_distr_params.py index 280b83f24ad3..c2d7f05e8360 100644 --- a/scipy/stats/_distr_params.py +++ b/scipy/stats/_distr_params.py @@ -87,6 +87,7 @@ ['norminvgauss', (1.25, 0.5)], ['pareto', (2.621716532144454,)], ['pearson3', (0.1,)], + ['pearson3', (-2,)], ['powerlaw', (1.6591133289905851,)], ['powerlognorm', (2.1413923530064087, 0.44639540782048337)], ['powernorm', (4.4453652254590779,)], diff --git a/scipy/stats/tests/test_distributions.py b/scipy/stats/tests/test_distributions.py index 4d49034009bb..e8f899832ab0 100644 --- a/scipy/stats/tests/test_distributions.py +++ b/scipy/stats/tests/test_distributions.py @@ -1788,6 +1788,25 @@ def test_return_array_bug_11746(self): assert_equal(moment, 0) assert_equal(type(moment), float) + def test_ppf_bug_17050(self): + # incorrect PPF for negative skews were reported in gh-17050 + # Check that this is fixed (even in the array case) + skews = [-3, -1, 0, 0.5] + x_eval = 0.5 + res = stats.pearson3.ppf(stats.pearson3.cdf(x_eval, skews), skews) + assert_allclose(res, x_eval) + + # Negation of the skew flips the distribution about the origin, so + # the following should hold + skew = np.array([[-0.5], [1.5]]) + x = np.linspace(-2, 2) + assert_allclose(stats.pearson3.pdf(x, skew), + stats.pearson3.pdf(-x, -skew)) + assert_allclose(stats.pearson3.cdf(x, skew), + stats.pearson3.sf(-x, -skew)) + assert_allclose(stats.pearson3.ppf(x, skew), + -stats.pearson3.isf(x, -skew)) + class TestKappa4: def test_cdf_genpareto(self): diff --git a/scipy/stats/tests/test_fit.py b/scipy/stats/tests/test_fit.py index 143cc4d23daa..c210b477a293 100644 --- a/scipy/stats/tests/test_fit.py +++ b/scipy/stats/tests/test_fit.py @@ -377,8 +377,8 @@ def test_basic_fit(self, dist_name): dist = getattr(stats, dist_name) shapes = np.array(dist_data[dist_name]) bounds = np.empty((len(shapes) + 2, 2), dtype=np.float64) - bounds[:-2, 0] = shapes/10**np.sign(shapes) - bounds[:-2, 1] = shapes*10**np.sign(shapes) + bounds[:-2, 0] = shapes/10.**np.sign(shapes) + bounds[:-2, 1] = shapes*10.**np.sign(shapes) bounds[-2] = (0, 10) bounds[-1] = (0, 10) loc = rng.uniform(*bounds[-2])