Skip to content

Commit 1d50e6e

Browse files
authored
Merge pull request scipy#21335 from WarrenWeckesser/boost-cauchy
BUG/ENH: stats: updates for cauchy.
2 parents 87ed0ca + d58a329 commit 1d50e6e

File tree

6 files changed

+143
-5
lines changed

6 files changed

+143
-5
lines changed

scipy/special/_add_newdocs.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11922,6 +11922,50 @@ def add_newdoc(name, doc):
1192211922
1192311923
""")
1192411924

11925+
add_newdoc(
11926+
"_cauchy_ppf",
11927+
"""
11928+
_cauchy_ppf(p, loc, scale)
11929+
11930+
Percent point function (i.e. quantile) of the Cauchy distribution.
11931+
11932+
Parameters
11933+
----------
11934+
p : array_like
11935+
Probabilities
11936+
loc : array_like
11937+
Location parameter of the distribution.
11938+
scale : array_like
11939+
Scale parameter of the distribution.
11940+
11941+
Returns
11942+
-------
11943+
scalar or ndarray
11944+
11945+
""")
11946+
11947+
add_newdoc(
11948+
"_cauchy_isf",
11949+
"""
11950+
_cauchy_isf(p, loc, scale)
11951+
11952+
Inverse survival function of the Cauchy distribution.
11953+
11954+
Parameters
11955+
----------
11956+
p : array_like
11957+
Probabilities
11958+
loc : array_like
11959+
Location parameter of the distribution.
11960+
scale : array_like
11961+
Scale parameter of the distribution.
11962+
11963+
Returns
11964+
-------
11965+
scalar or ndarray
11966+
11967+
""")
11968+
1192511969
add_newdoc(
1192611970
"_ncx2_pdf",
1192711971
"""

scipy/special/boost_special_functions.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,46 @@ invgauss_isf_double(double x, double mu, double s)
588588
return invgauss_isf_wrap(x, mu, s);
589589
}
590590

591+
template<typename Real>
592+
Real
593+
cauchy_ppf_wrap(const Real p, const Real loc, const Real scale)
594+
{
595+
return boost::math::quantile(
596+
boost::math::cauchy_distribution<Real, StatsPolicy>(loc, scale), p);
597+
}
598+
599+
float
600+
cauchy_ppf_float(float p, float loc, float scale)
601+
{
602+
return cauchy_ppf_wrap(p, loc, scale);
603+
}
604+
605+
double
606+
cauchy_ppf_double(double p, double loc, double scale)
607+
{
608+
return cauchy_ppf_wrap(p, loc, scale);
609+
}
610+
611+
template<typename Real>
612+
Real
613+
cauchy_isf_wrap(const Real p, const Real loc, const Real scale)
614+
{
615+
return boost::math::quantile(boost::math::complement(
616+
boost::math::cauchy_distribution<Real, StatsPolicy>(loc, scale), p));
617+
}
618+
619+
float
620+
cauchy_isf_float(float p, float loc, float scale)
621+
{
622+
return cauchy_isf_wrap(p, loc, scale);
623+
}
624+
625+
double
626+
cauchy_isf_double(double p, double loc, double scale)
627+
{
628+
return cauchy_isf_wrap(p, loc, scale);
629+
}
630+
591631
template<typename Real>
592632
Real
593633
ncx2_pdf_wrap(const Real x, const Real k, const Real l)

scipy/special/functions.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,18 @@
10331033
"invgauss_isf_double": "ddd->d"
10341034
}
10351035
},
1036+
"_cauchy_ppf": {
1037+
"boost_special_functions.h++": {
1038+
"cauchy_ppf_float": "fff->f",
1039+
"cauchy_ppf_double": "ddd->d"
1040+
}
1041+
},
1042+
"_cauchy_isf": {
1043+
"boost_special_functions.h++": {
1044+
"cauchy_isf_float": "fff->f",
1045+
"cauchy_isf_double": "ddd->d"
1046+
}
1047+
},
10361048
"_ncx2_pdf": {
10371049
"boost_special_functions.h++": {
10381050
"ncx2_pdf_float": "fff->f",

scipy/stats/_continuous_distns.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,16 +1454,16 @@ def _pdf(self, x):
14541454
return 1.0/np.pi/(1.0+x*x)
14551455

14561456
def _cdf(self, x):
1457-
return 0.5 + 1.0/np.pi*np.arctan(x)
1457+
return np.arctan2(1, -x)/np.pi
14581458

14591459
def _ppf(self, q):
1460-
return np.tan(np.pi*q-np.pi/2.0)
1460+
return scu._cauchy_ppf(q, 0, 1)
14611461

14621462
def _sf(self, x):
1463-
return 0.5 - 1.0/np.pi*np.arctan(x)
1463+
return np.arctan2(1, x)/np.pi
14641464

14651465
def _isf(self, q):
1466-
return np.tan(np.pi/2.0-np.pi*q)
1466+
return scu._cauchy_isf(q, 0, 1)
14671467

14681468
def _stats(self):
14691469
return np.nan, np.nan, np.nan, np.nan

scipy/stats/tests/test_continuous_basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
# Here 'fail' mean produce wrong results and/or raise exceptions, depending
7676
# on the implementation details of corresponding special functions.
7777
# cf https://github.com/scipy/scipy/pull/4979 for a discussion.
78-
fails_cmplx = {'argus', 'beta', 'betaprime', 'chi', 'chi2', 'cosine',
78+
fails_cmplx = {'argus', 'beta', 'betaprime', 'cauchy', 'chi', 'chi2', 'cosine',
7979
'dgamma', 'dweibull', 'erlang', 'f', 'foldcauchy', 'gamma',
8080
'gausshyper', 'gengamma', 'genhyperbolic',
8181
'geninvgauss', 'gennorm', 'genpareto',

scipy/stats/tests/test_distributions.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,48 @@ def test_cdf_ppf(self):
507507
assert_allclose(x, xx)
508508

509509

510+
class TestCauchy:
511+
512+
# Reference values were computed with mpmath.
513+
@pytest.mark.parametrize(
514+
'x, ref',
515+
[(-5e15, 6.366197723675814e-17),
516+
(-5, 0.06283295818900118),
517+
(-1, 0.25),
518+
(0, 0.5),
519+
(1, 0.75),
520+
(5, 0.9371670418109989),
521+
(5e15, 0.9999999999999999)]
522+
)
523+
@pytest.mark.parametrize(
524+
'method, sgn',
525+
[(stats.cauchy.cdf, 1),
526+
(stats.cauchy.sf, -1)]
527+
)
528+
def test_cdf_sf(self, x, ref, method, sgn):
529+
p = method(sgn*x)
530+
assert_allclose(p, ref, rtol=1e-15)
531+
532+
# Reference values were computed with mpmath.
533+
@pytest.mark.parametrize(
534+
'p, ref',
535+
[(1e-20, -3.1830988618379067e+19),
536+
(1e-9, -318309886.1837906),
537+
(0.25, -1.0),
538+
(0.50, 0.0),
539+
(0.75, 1.0),
540+
(0.999999, 318309.88617359026),
541+
(0.999999999999, 318316927901.77966)]
542+
)
543+
@pytest.mark.parametrize(
544+
'method, sgn',
545+
[(stats.cauchy.ppf, 1),
546+
(stats.cauchy.isf, -1)])
547+
def test_ppf_isf(self, p, ref, method, sgn):
548+
x = sgn*method(p)
549+
assert_allclose(x, ref, rtol=1e-15)
550+
551+
510552
class TestChi:
511553

512554
# "Exact" value of chi.sf(10, 4), as computed by Wolfram Alpha with

0 commit comments

Comments
 (0)