Skip to content

Commit 821da97

Browse files
committed
BUG: fix sqrt(negative number) issue in rolling_std close #1840
1 parent 859aa2c commit 821da97

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

RELEASE.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ pandas 0.8.2
9898
right time zone (#1777)
9999
- Fix DST issues with generating anchored date ranges (#1778)
100100
- Fix issue calling sort on result of Series.unique (#1807)
101+
- Fix numerical issue leading to square root of negative number in
102+
rolling_std (#1840)
101103

102104
pandas 0.8.1
103105
============

pandas/stats/moments.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def ewmstd(arg, com=None, span=None, min_periods=0, bias=False,
309309
time_rule=None):
310310
result = ewmvar(arg, com=com, span=span, time_rule=time_rule,
311311
min_periods=min_periods, bias=bias)
312-
return np.sqrt(result)
312+
return _zsqrt(result)
313313

314314
ewmvol = ewmstd
315315

@@ -343,7 +343,21 @@ def ewmcorr(arg1, arg2, com=None, span=None, min_periods=0,
343343
mean = lambda x: ewma(x, com=com, span=span, min_periods=min_periods)
344344
var = lambda x: ewmvar(x, com=com, span=span, min_periods=min_periods,
345345
bias=True)
346-
return (mean(X*Y) - mean(X)*mean(Y)) / np.sqrt(var(X) * var(Y))
346+
return (mean(X*Y) - mean(X)*mean(Y)) / _zsqrt(var(X) * var(Y))
347+
348+
349+
def _zsqrt(x):
350+
result = np.sqrt(x)
351+
mask = x < 0
352+
353+
if isinstance(x, DataFrame):
354+
if mask.values.any():
355+
result[mask] = 0
356+
else:
357+
if mask.any():
358+
result[mask] = 0
359+
360+
return result
347361

348362
def _prep_binary(arg1, arg2):
349363
if not isinstance(arg2, type(arg1)):
@@ -406,7 +420,7 @@ def call_cython(arg, window, minp, **kwds):
406420
rolling_mean = _rolling_func(lib.roll_mean, 'Moving mean')
407421
rolling_median = _rolling_func(lib.roll_median_cython, 'Moving median')
408422

409-
_ts_std = lambda *a, **kw: np.sqrt(lib.roll_var(*a, **kw))
423+
_ts_std = lambda *a, **kw: _zsqrt(lib.roll_var(*a, **kw))
410424
rolling_std = _rolling_func(_ts_std, 'Unbiased moving standard deviation',
411425
check_minp=_require_min_periods(2))
412426
rolling_var = _rolling_func(lib.roll_var, 'Unbiased moving variance',

pandas/stats/tests/test_moments.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ def test_rolling_std(self):
8989
self._check_moment_func(functools.partial(mom.rolling_std, ddof=0),
9090
lambda x: np.std(x, ddof=0))
9191

92+
def test_rolling_std_neg_sqrt(self):
93+
# unit test from Bottleneck
94+
95+
# Test move_nanstd for neg sqrt.
96+
97+
a = np.array([0.0011448196318903589,
98+
0.00028718669878572767,
99+
0.00028718669878572767,
100+
0.00028718669878572767,
101+
0.00028718669878572767])
102+
b = mom.rolling_std(a, window=3)
103+
self.assert_(np.isfinite(b[2:]).all())
104+
105+
b = mom.ewmstd(a, span=3)
106+
self.assert_(np.isfinite(b[2:]).all())
107+
92108
def test_rolling_var(self):
93109
self._check_moment_func(mom.rolling_var,
94110
lambda x: np.var(x, ddof=1))

0 commit comments

Comments
 (0)