Skip to content

Commit

Permalink
ENH: stats.vonmises.fit: treat case in which location likelihood equa…
Browse files Browse the repository at this point in the history
…tion has no solution(#18190)

* ENH: stats.vonmises.fit: treat case in which location likelihood equation has no solution
  • Loading branch information
dschmitz89 committed Mar 25, 2023
1 parent 9562a4a commit 979102e
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 5 deletions.
22 changes: 17 additions & 5 deletions scipy/stats/_continuous_distns.py
Original file line number Diff line number Diff line change
Expand Up @@ -10029,12 +10029,24 @@ def find_kappa(data, loc):
r = np.sum(np.cos(loc - data))/len(data)
# See gh-18128 for more information.

def solve_for_kappa(kappa):
return sc.i1e(kappa)/sc.i0e(kappa) - r
if r > 0:
def solve_for_kappa(kappa):
return sc.i1e(kappa)/sc.i0e(kappa) - r

root_res = root_scalar(solve_for_kappa, method="brentq",
bracket=(1e-8, 1e12))
return root_res.root
root_res = root_scalar(solve_for_kappa, method="brentq",
bracket=(1e-8, 1e12))
return root_res.root
else:
# if the provided floc is very far from the circular mean,
# the mean resultant length r can become negative.
# In that case, the equation
# I[1](kappa)/I[0](kappa) = r does not have a solution.
# The maximum likelihood kappa is then 0 which practically
# results in the uniform distribution on the circle. As
# vonmises is defined for kappa > 0, return instead the
# smallest floating point value.
# See gh-18190 for more information
return np.finfo(float).tiny

# location likelihood equation has a solution independent of kappa
loc = floc if floc is not None else find_mu(data)
Expand Down
13 changes: 13 additions & 0 deletions scipy/stats/tests/test_distributions.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,19 @@ def test_vonmises_fit_shape(self):
assert scale_fit == 1
assert_allclose(kappa, kappa_fit, rtol=1e-2)

@pytest.mark.xslow
@pytest.mark.parametrize('loc', [-0.5 * np.pi, -0.9 * np.pi])
def test_vonmises_fit_bad_floc(self, loc):
data = [-0.92923506, -0.32498224, 0.13054989, -0.97252014, 2.79658071,
-0.89110948, 1.22520295, 1.44398065, 2.49163859, 1.50315096,
3.05437696, -2.73126329, -3.06272048, 1.64647173, 1.94509247,
-1.14328023, 0.8499056, 2.36714682, -1.6823179, -0.88359996]
data = np.asarray(data)
kappa_fit, loc_fit, scale_fit = stats.vonmises.fit(data, floc=loc)
assert kappa_fit == np.finfo(float).tiny
_assert_less_or_close_loglike(stats.vonmises, data,
stats.vonmises.nnlf, fscale=1, floc=loc)

@pytest.mark.parametrize('sign', [-1, 1])
def test_vonmises_fit_unwrapped_data(self, sign):
rng = np.random.default_rng(6762668991392531563)
Expand Down

0 comments on commit 979102e

Please sign in to comment.