From 9a0e52a7bdedf4c66e2a0d79dec06f8fe05cc830 Mon Sep 17 00:00:00 2001 From: Wes McKinney Date: Thu, 12 Jul 2012 23:03:13 -0400 Subject: [PATCH] ENH: add adjust option to ewma to disable adjustment close #1584 --- RELEASE.rst | 1 + pandas/src/moments.pyx | 13 ++++++------ pandas/stats/moments.py | 33 +++++++++++++++++------------- pandas/stats/tests/test_moments.py | 5 +++++ 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/RELEASE.rst b/RELEASE.rst index e65597d8a6f62..7b57c018683eb 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -53,6 +53,7 @@ pandas 0.8.1 to PeriodIndex inside Index (#1215) - More informative string representation for weekly Period objects (#1503) - Accelerate 3-axis multi data selection from homogeneous Panel (#979) + - Add ``adjust`` option to ewma to disable adjustment factor (#1584) **Bug fixes** diff --git a/pandas/src/moments.pyx b/pandas/src/moments.pyx index 6e37b6807fd13..9a7bfa904c3fe 100644 --- a/pandas/src/moments.pyx +++ b/pandas/src/moments.pyx @@ -227,7 +227,7 @@ def roll_mean(ndarray[double_t] input, #------------------------------------------------------------------------------- # Exponentially weighted moving average -def ewma(ndarray[double_t] input, double_t com): +def ewma(ndarray[double_t] input, double_t com, int adjust): ''' Compute exponentially-weighted moving average using center-of-mass. @@ -266,12 +266,13 @@ def ewma(ndarray[double_t] input, double_t com): else: output[i] = prev - for i from 0 <= i < N: - cur = input[i] - output[i] = output[i] / (1. - adj) + if adjust: + for i from 0 <= i < N: + cur = input[i] + output[i] = output[i] / (1. - adj) - if cur == cur: - adj *= oldw + if cur == cur: + adj *= oldw return output diff --git a/pandas/stats/moments.py b/pandas/stats/moments.py index 35a994f01eb95..29df509832ea6 100644 --- a/pandas/stats/moments.py +++ b/pandas/stats/moments.py @@ -10,7 +10,7 @@ import numpy as np from pandas.core.api import DataFrame, Series, notnull -import pandas.lib as _tseries +import pandas.lib as lib from pandas.util.decorators import Substitution, Appender @@ -56,6 +56,10 @@ beginning) freq : None or string alias / date offset object, default=None Frequency to conform to before computing statistic +adjust : boolean, default True + Divide by decaying adjustment factor in beginning periods to account for + imbalance in relative weightings (viewing EWMA as a moving average) + %s Notes ----- @@ -265,12 +269,13 @@ def _get_center_of_mass(com, span): @Substitution("Exponentially-weighted moving average", _unary_arg, "") @Appender(_ewm_doc) -def ewma(arg, com=None, span=None, min_periods=0, freq=None, time_rule=None): +def ewma(arg, com=None, span=None, min_periods=0, freq=None, time_rule=None, + adjust=True): com = _get_center_of_mass(com, span) arg = _conv_timerule(arg, freq, time_rule) def _ewma(v): - result = _tseries.ewma(v, com) + result = lib.ewma(v, com, int(adjust)) first_index = _first_valid_index(v) result[first_index : first_index + min_periods] = NaN return result @@ -395,20 +400,20 @@ def call_cython(arg, window, minp, **kwds): return f -rolling_max = _rolling_func(_tseries.roll_max2, 'Moving maximum') -rolling_min = _rolling_func(_tseries.roll_min2, 'Moving minimum') -rolling_sum = _rolling_func(_tseries.roll_sum, 'Moving sum') -rolling_mean = _rolling_func(_tseries.roll_mean, 'Moving mean') -rolling_median = _rolling_func(_tseries.roll_median_cython, 'Moving median') +rolling_max = _rolling_func(lib.roll_max2, 'Moving maximum') +rolling_min = _rolling_func(lib.roll_min2, 'Moving minimum') +rolling_sum = _rolling_func(lib.roll_sum, 'Moving sum') +rolling_mean = _rolling_func(lib.roll_mean, 'Moving mean') +rolling_median = _rolling_func(lib.roll_median_cython, 'Moving median') -_ts_std = lambda *a, **kw: np.sqrt(_tseries.roll_var(*a, **kw)) +_ts_std = lambda *a, **kw: np.sqrt(lib.roll_var(*a, **kw)) rolling_std = _rolling_func(_ts_std, 'Unbiased moving standard deviation', check_minp=_require_min_periods(2)) -rolling_var = _rolling_func(_tseries.roll_var, 'Unbiased moving variance', +rolling_var = _rolling_func(lib.roll_var, 'Unbiased moving variance', check_minp=_require_min_periods(2)) -rolling_skew = _rolling_func(_tseries.roll_skew, 'Unbiased moving skewness', +rolling_skew = _rolling_func(lib.roll_skew, 'Unbiased moving skewness', check_minp=_require_min_periods(3)) -rolling_kurt = _rolling_func(_tseries.roll_kurt, 'Unbiased moving kurtosis', +rolling_kurt = _rolling_func(lib.roll_kurt, 'Unbiased moving kurtosis', check_minp=_require_min_periods(4)) def rolling_quantile(arg, window, quantile, min_periods=None, freq=None, @@ -432,7 +437,7 @@ def rolling_quantile(arg, window, quantile, min_periods=None, freq=None, def call_cython(arg, window, minp): minp = _use_window(minp, window) - return _tseries.roll_quantile(arg, window, minp, quantile) + return lib.roll_quantile(arg, window, minp, quantile) return _rolling_moment(arg, window, call_cython, min_periods, freq=freq, time_rule=time_rule) @@ -457,6 +462,6 @@ def rolling_apply(arg, window, func, min_periods=None, freq=None, """ def call_cython(arg, window, minp): minp = _use_window(minp, window) - return _tseries.roll_generic(arg, window, minp, func) + return lib.roll_generic(arg, window, minp, func) return _rolling_moment(arg, window, call_cython, min_periods, freq=freq, time_rule=time_rule) diff --git a/pandas/stats/tests/test_moments.py b/pandas/stats/tests/test_moments.py index ff13385e591ed..605720fa6837f 100644 --- a/pandas/stats/tests/test_moments.py +++ b/pandas/stats/tests/test_moments.py @@ -219,6 +219,11 @@ def test_legacy_time_rule_arg(self): def test_ewma(self): self._check_ew(mom.ewma) + arr = np.zeros(1000) + arr[5] = 1 + result = mom.ewma(arr, span=100, adjust=False).sum() + self.assert_(np.abs(result - 1) < 1e-2) + def test_ewmvar(self): self._check_ew(mom.ewmvar)