Skip to content

Commit 7705027

Browse files
committed
Merge pull request #7896 from seth-p/ewma_missing_weights_fix1
BUG: fixing ewma() for adjust=False and ignore_na=False
2 parents 9f42640 + 1fcb6c6 commit 7705027

File tree

3 files changed

+38
-13
lines changed

3 files changed

+38
-13
lines changed

pandas/algos.pyx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,10 @@ def ewma(ndarray[double_t] input, double_t com, int adjust, int ignore_na):
10181018
if cur == cur:
10191019
old_wt *= old_wt_factor
10201020
weighted_avg = ((old_wt * weighted_avg) + (new_wt * cur)) / (old_wt + new_wt)
1021-
old_wt += new_wt
1021+
if adjust:
1022+
old_wt += new_wt
1023+
else:
1024+
old_wt = 1.
10221025
elif not ignore_na:
10231026
old_wt *= old_wt_factor
10241027
else:

pandas/stats/moments.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@
107107
:math:`c = (s - 1) / 2`
108108
109109
So a "20-day EWMA" would have center 9.5.
110+
111+
When adjust is True (default), weighted averages are calculated using weights
112+
(1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.
113+
114+
When adjust is False, weighted averages are calculated recursively as:
115+
weighted_average[0] = arg[0];
116+
weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].
117+
118+
When ignore_na is False (default), weights are based on absolute positions.
119+
For example, the weights of x and y used in calculating the final weighted
120+
average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
121+
(1-alpha)**2 and alpha (if adjust is False).
122+
123+
When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based on
124+
relative positions. For example, the weights of x and y used in calculating
125+
the final weighted average of [x, None, y] are 1-alpha and 1 (if adjust is
126+
True), and 1-alpha and alpha (if adjust is False).
110127
"""
111128

112129
_expanding_kw = """min_periods : int, default None

pandas/stats/tests/test_moments.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -551,25 +551,30 @@ def test_ewma_nan_handling(self):
551551
s0 = Series([np.nan, 1., 101.])
552552
s1 = Series([1., np.nan, 101.])
553553
s2 = Series([np.nan, 1., np.nan, np.nan, 101., np.nan])
554+
s3 = Series([1., np.nan, 101., 50.])
554555
com = 2.
555556
alpha = 1. / (1. + com)
556557

557558
def simple_wma(s, w):
558559
return (s.multiply(w).cumsum() / w.cumsum()).fillna(method='ffill')
559560

560561
for (s, adjust, ignore_na, w) in [
561-
(s0, True, False, [np.nan, (1.0 - alpha), 1.]),
562-
(s0, True, True, [np.nan, (1.0 - alpha), 1.]),
563-
(s0, False, False, [np.nan, (1.0 - alpha), alpha]),
564-
(s0, False, True, [np.nan, (1.0 - alpha), alpha]),
565-
(s1, True, False, [(1.0 - alpha)**2, np.nan, 1.]),
566-
(s1, True, True, [(1.0 - alpha), np.nan, 1.]),
567-
(s1, False, False, [(1.0 - alpha)**2, np.nan, alpha]),
568-
(s1, False, True, [(1.0 - alpha), np.nan, alpha]),
569-
(s2, True, False, [np.nan, (1.0 - alpha)**3, np.nan, np.nan, 1., np.nan]),
570-
(s2, True, True, [np.nan, (1.0 - alpha), np.nan, np.nan, 1., np.nan]),
571-
(s2, False, False, [np.nan, (1.0 - alpha)**3, np.nan, np.nan, alpha, np.nan]),
572-
(s2, False, True, [np.nan, (1.0 - alpha), np.nan, np.nan, alpha, np.nan]),
562+
(s0, True, False, [np.nan, (1. - alpha), 1.]),
563+
(s0, True, True, [np.nan, (1. - alpha), 1.]),
564+
(s0, False, False, [np.nan, (1. - alpha), alpha]),
565+
(s0, False, True, [np.nan, (1. - alpha), alpha]),
566+
(s1, True, False, [(1. - alpha)**2, np.nan, 1.]),
567+
(s1, True, True, [(1. - alpha), np.nan, 1.]),
568+
(s1, False, False, [(1. - alpha)**2, np.nan, alpha]),
569+
(s1, False, True, [(1. - alpha), np.nan, alpha]),
570+
(s2, True, False, [np.nan, (1. - alpha)**3, np.nan, np.nan, 1., np.nan]),
571+
(s2, True, True, [np.nan, (1. - alpha), np.nan, np.nan, 1., np.nan]),
572+
(s2, False, False, [np.nan, (1. - alpha)**3, np.nan, np.nan, alpha, np.nan]),
573+
(s2, False, True, [np.nan, (1. - alpha), np.nan, np.nan, alpha, np.nan]),
574+
(s3, True, False, [(1. - alpha)**3, np.nan, (1. - alpha), 1.]),
575+
(s3, True, True, [(1. - alpha)**2, np.nan, (1. - alpha), 1.]),
576+
(s3, False, False, [(1. - alpha)**3, np.nan, (1. - alpha) * alpha, alpha * ((1. - alpha)**2 + alpha)]),
577+
(s3, False, True, [(1. - alpha)**2, np.nan, (1. - alpha) * alpha, alpha]),
573578
]:
574579
expected = simple_wma(s, Series(w))
575580
result = mom.ewma(s, com=com, adjust=adjust, ignore_na=ignore_na)

0 commit comments

Comments
 (0)