From db78efb3d39d525caae4a6dd66b6b6f5effb5a6f Mon Sep 17 00:00:00 2001 From: jreback Date: Mon, 18 Mar 2013 19:00:40 -0400 Subject: [PATCH] BUG: fixed up cmoment functions BUG: fixed rolling_kurt/skew --- pandas/algos.pyx | 17 +++-- pandas/stats/moments.py | 101 +++++++++++++++++++---------- pandas/stats/tests/test_moments.py | 2 + 3 files changed, 78 insertions(+), 42 deletions(-) diff --git a/pandas/algos.pyx b/pandas/algos.pyx index cac9c5ccc7a6d..829059c56b705 100644 --- a/pandas/algos.pyx +++ b/pandas/algos.pyx @@ -1104,8 +1104,11 @@ def roll_skew(ndarray[double_t] input, int win, int minp): R = sqrt(B) - output[i] = ((sqrt(nobs * (nobs - 1.)) * C) / - ((nobs-2) * R * R * R)) + if R != 0 and nobs > 2: + output[i] = ((sqrt(nobs * (nobs - 1.)) * C) / + ((nobs-2) * R * R * R)) + else: + output[i] = NaN else: output[i] = NaN @@ -1173,10 +1176,12 @@ def roll_kurt(ndarray[double_t] input, R = R * A D = xxxx / nobs - R - 6*B*A*A - 4*C*A - K = (nobs * nobs - 1.)*D/(B*B) - 3*((nobs-1.)**2) - K = K / ((nobs - 2.)*(nobs-3.)) - - output[i] = K + if B != 0 and nobs > 3: + K = (nobs * nobs - 1.)*D/(B*B) - 3*((nobs-1.)**2) + K = K / ((nobs - 2.)*(nobs-3.)) + output[i] = K + else: + output[i] = NaN else: output[i] = NaN diff --git a/pandas/stats/moments.py b/pandas/stats/moments.py index ba823bb2ce59b..9c719c0562c47 100644 --- a/pandas/stats/moments.py +++ b/pandas/stats/moments.py @@ -148,12 +148,17 @@ def rolling_count(arg, window, freq=None, center=False, time_rule=None): return_hook, values = _process_data_structure(arg, kill_inf=False) converted = np.isfinite(values).astype(float) - result = rolling_sum(converted, window, min_periods=1, - center=center) # already converted + if center: + center = ('na','na') + converted = _center_window(converted, window, 0, center=center) + result = rolling_sum(converted, window, min_periods=1, + center=False) + else: - # putmask here? - result[np.isnan(result)] = 0 + result = rolling_sum(converted, window, min_periods=1, + center=center) + result[np.isnan(result)] = 0 return return_hook(result) @@ -277,50 +282,58 @@ def _rolling_moment(arg, window, func, minp, axis=0, freq=None, y : type of input """ arg = _conv_timerule(arg, freq, time_rule) - calc = lambda x: func(x, window, minp=minp, **kwargs) return_hook, values = _process_data_structure(arg) + # actually calculate the moment. Faster way to do this? - if values.ndim > 1: - result = np.apply_along_axis(calc, axis, values) - else: - result = calc(values) + def calc(x): + _calc = lambda x: func(x, window, minp=minp, **kwargs) + if x.ndim > 1: + return np.apply_along_axis(_calc, axis, x) + else: + return _calc(x) + result = calc(values) rs = return_hook(result) if center: - rs = _center_window(rs, window, axis) + rs = _center_window(rs, window, axis, center=center) # GH2953, fixup edges if window > 2: - if values.ndim > 1: - # TODO: handle mi vectorized case - pass + + # there's an ambiguity on what constitutes + # the "center" when window is even + # we Just close ranks with numpy , see test case + # delta = 1 if window % 2 == 0 else 0 + if window % 2 == 0 : + nahead = (window-1)//2 or 1 else: - # there's an ambiguity on what constitutes - # the "center" when window is even - # we Just close ranks with numpy , see test case - # delta = 1 if window % 2 == 0 else 0 - if window % 2 == 0 : - nahead = (window-1)//2 or 1 - else: - nahead = (window)//2 - - # fixup the head - tip = np.append(np.zeros(nahead+1),values[:(2*nahead+1)]) - rs[:nahead+1] = calc(tip)[-(nahead+1):][:nahead+1] - - # fixup the tail - tip = np.append(values[-(2*nahead+1):],np.zeros(nahead)) - rs[-(nahead):] = calc(tip)[-(nahead):] - - if minp > 0: - d = minp - nahead-1 - if d > 0: - rs[:d] = NaN - rs[-(d):] = NaN + nahead = (window)//2 + + # fixup the head + shape = list(values.shape) + shape[0] = nahead+1 + tip = np.append(np.zeros(tuple(shape)),values[:(2*nahead+1)],axis=0) + rs[:nahead+1] = calc(tip)[-(nahead+1):][:nahead+1] + if minp > 0: + d = max(minp,nahead+1) + if d > 0: + rs[:d] = NaN + + + # fixup the tail + shape = list(values.shape) + shape[0] = nahead + tip = np.append(values[-(2*nahead+1):],np.zeros(tuple(shape)),axis=0) + rs[-(nahead):] = calc(tip)[-(nahead):] + + if minp > 0: + d = max(nahead,minp) + if d > 0: + rs[-d:] = NaN return rs -def _center_window(rs, window, axis): +def _center_window(rs, window, axis, center=None): offset = int((window - 1) / 2.) if isinstance(rs, (Series, DataFrame, Panel)): rs = rs.shift(-offset, axis=axis) @@ -335,7 +348,23 @@ def _center_window(rs, window, axis): na_indexer[axis] = slice(-offset, None) rs[tuple(rs_indexer)] = np.copy(rs[tuple(lead_indexer)]) + + # center can optionally point to an action on + # lhs or rhs + + if not isinstance(center, tuple): + center = ('copy','na') + lhs, rhs = center + + # lhs : default is copy + if lhs == 'na': + lhs_indexer = [slice(None)] * rs.ndim + lhs_indexer[axis] = slice(0, offset+1) + rs[tuple(lhs_indexer)] = np.nan + + # rhs : default is na rs[tuple(na_indexer)] = np.nan + return rs diff --git a/pandas/stats/tests/test_moments.py b/pandas/stats/tests/test_moments.py index a155b9ef060ef..60a107d3ab57d 100644 --- a/pandas/stats/tests/test_moments.py +++ b/pandas/stats/tests/test_moments.py @@ -442,7 +442,9 @@ def _check_structures(self, func, static_comp, if has_min_periods: minp = 10 series_xp = func(self.series, 25, min_periods=minp).shift(-12) + series_xp[:13] = np.nan frame_xp = func(self.frame, 25, min_periods=minp).shift(-12) + frame_xp[:13] = np.nan series_rs = func(self.series, 25, min_periods=minp, center=True)