Skip to content

Commit b50de1b

Browse files
committed
BUG: fix robust.norm.Hampel
1 parent c967ca0 commit b50de1b

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

statsmodels/robust/norms.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -629,15 +629,21 @@ def rho(self, z):
629629
"""
630630

631631
z = np.abs(z)
632-
a = self.a
633-
b = self.b
634-
c = self.c
632+
z_isscalar = np.isscalar(z)
633+
z = np.atleast_1d(z)
634+
a = self.a; b = self.b; c = self.c
635635
t1, t2, t3 = self._subset(z)
636-
v = (t1 * z**2 * 0.5 +
637-
t2 * (a * z - a**2 * 0.5) +
638-
t3 * (a * (c * z - z**2 * 0.5) / (c - b) - 7 * a**2 / 6.) +
639-
(1 - t1 + t2 + t3) * a * (b + c - a))
640-
return v
636+
t34 = ~(t1 | t2 )
637+
v = np.zeros(z.shape, float)
638+
v[t1] = z[t1]**2 * 0.5
639+
v[t2] = (a * (z[t2] - a) + a**2 * 0.5)
640+
v[t3] = a * (c - z[t3])**2 / (c - b) * (-0.5)
641+
v[t34] += a * (b + c - a) * 0.5
642+
643+
if z_isscalar:
644+
return v[0]
645+
else:
646+
return v
641647

642648
def psi(self, z):
643649
r"""
@@ -662,13 +668,11 @@ def psi(self, z):
662668
psi(z) = 0 for \|z\| > c
663669
"""
664670
z = np.asarray(z)
665-
a = self.a
666-
b = self.b
667-
c = self.c
671+
a = self.a; b = self.b; c = self.c
668672
t1, t2, t3 = self._subset(z)
669673
s = np.sign(z)
670674
z = np.abs(z)
671-
v = s * (t1 * z +
675+
v = (t1 * z*s +
672676
t2 * a*s +
673677
t3 * a*s * (c - z) / (c - b))
674678
return v
@@ -711,13 +715,16 @@ def weights(self, z):
711715
return v
712716

713717
def psi_deriv(self, z):
718+
"""Derivative of psi function, second derivative of rho function.
719+
"""
714720
t1, _, t3 = self._subset(z)
715721
a, b, c = self.a, self.b, self.c
722+
z = np.atleast_1d(z)
716723
# default is t1
717724
d = np.zeros_like(z)
718725
d[t1] = 1.0
719726
zt3 = z[t3]
720-
d[t3] = (a * np.sign(zt3) * zt3) / (np.abs(zt3) * (c - b))
727+
d[t3] = -(a * np.sign(zt3) * zt3) / (np.abs(zt3) * (c - b))
721728
return d
722729

723730

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
3+
from statsmodels.tools.testing import Holder
4+
5+
res_hampel = Holder(
6+
rho=[7.5, 6.833333333333333, 1.875, 0.5, 0.0, 0.5, 1.875,
7+
6.833333333333333, 7.5],
8+
psi=[0.0, -0.6666666666666666, -1.5, -1.0, 0.0, 1.0, 1.5,
9+
0.6666666666666666, 0.0],
10+
psi_deriv=[0.0, -0.3333333333333333, 0.0, 1.0, 1.0, 1.0, 0.0,
11+
-0.3333333333333333, 0.0],
12+
weights=[0.0, 0.1111111111111111, 0.75, 1.0, 1.0, 1.0, 0.75,
13+
0.1111111111111111, 0.0],
14+
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
import pytest
3+
import numpy as np
4+
from numpy.testing import assert_allclose
5+
6+
from statsmodels.robust import norms
7+
from .results import results_norms as res_r
8+
9+
cases = [
10+
(norms.Hampel, (1.5, 3.5, 8.), res_r.res_hampel)
11+
]
12+
13+
@pytest.mark.parametrize("case", cases)
14+
def test_norm(case):
15+
ncls, args, res = case
16+
norm = ncls(*args)
17+
x = np.array([-9., -6, -2, -1, 0, 1, 2, 6, 9])
18+
19+
assert_allclose(norm.weights(x), res.weights, rtol=1e-12, atol=1e-20)
20+
assert_allclose(norm.rho(x), res.rho, rtol=1e-12, atol=1e-20)
21+
assert_allclose(norm.psi(x), res.psi, rtol=1e-12, atol=1e-20)
22+
assert_allclose(norm.psi_deriv(x), res.psi_deriv, rtol=1e-12, atol=1e-20)

0 commit comments

Comments
 (0)