Skip to content

Commit db78efb

Browse files
committed
BUG: fixed up cmoment functions
BUG: fixed rolling_kurt/skew
1 parent 71d7435 commit db78efb

File tree

3 files changed

+78
-42
lines changed

3 files changed

+78
-42
lines changed

pandas/algos.pyx

+11-6
Original file line numberDiff line numberDiff line change
@@ -1104,8 +1104,11 @@ def roll_skew(ndarray[double_t] input, int win, int minp):
11041104

11051105
R = sqrt(B)
11061106

1107-
output[i] = ((sqrt(nobs * (nobs - 1.)) * C) /
1108-
((nobs-2) * R * R * R))
1107+
if R != 0 and nobs > 2:
1108+
output[i] = ((sqrt(nobs * (nobs - 1.)) * C) /
1109+
((nobs-2) * R * R * R))
1110+
else:
1111+
output[i] = NaN
11091112
else:
11101113
output[i] = NaN
11111114

@@ -1173,10 +1176,12 @@ def roll_kurt(ndarray[double_t] input,
11731176
R = R * A
11741177
D = xxxx / nobs - R - 6*B*A*A - 4*C*A
11751178

1176-
K = (nobs * nobs - 1.)*D/(B*B) - 3*((nobs-1.)**2)
1177-
K = K / ((nobs - 2.)*(nobs-3.))
1178-
1179-
output[i] = K
1179+
if B != 0 and nobs > 3:
1180+
K = (nobs * nobs - 1.)*D/(B*B) - 3*((nobs-1.)**2)
1181+
K = K / ((nobs - 2.)*(nobs-3.))
1182+
output[i] = K
1183+
else:
1184+
output[i] = NaN
11801185
else:
11811186
output[i] = NaN
11821187

pandas/stats/moments.py

+65-36
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,17 @@ def rolling_count(arg, window, freq=None, center=False, time_rule=None):
148148
return_hook, values = _process_data_structure(arg, kill_inf=False)
149149

150150
converted = np.isfinite(values).astype(float)
151-
result = rolling_sum(converted, window, min_periods=1,
152-
center=center) # already converted
151+
if center:
152+
center = ('na','na')
153+
converted = _center_window(converted, window, 0, center=center)
154+
result = rolling_sum(converted, window, min_periods=1,
155+
center=False)
156+
else:
153157

154-
# putmask here?
155-
result[np.isnan(result)] = 0
158+
result = rolling_sum(converted, window, min_periods=1,
159+
center=center)
156160

161+
result[np.isnan(result)] = 0
157162
return return_hook(result)
158163

159164

@@ -277,50 +282,58 @@ def _rolling_moment(arg, window, func, minp, axis=0, freq=None,
277282
y : type of input
278283
"""
279284
arg = _conv_timerule(arg, freq, time_rule)
280-
calc = lambda x: func(x, window, minp=minp, **kwargs)
281285
return_hook, values = _process_data_structure(arg)
286+
282287
# actually calculate the moment. Faster way to do this?
283-
if values.ndim > 1:
284-
result = np.apply_along_axis(calc, axis, values)
285-
else:
286-
result = calc(values)
288+
def calc(x):
289+
_calc = lambda x: func(x, window, minp=minp, **kwargs)
290+
if x.ndim > 1:
291+
return np.apply_along_axis(_calc, axis, x)
292+
else:
293+
return _calc(x)
287294

295+
result = calc(values)
288296
rs = return_hook(result)
289297
if center:
290-
rs = _center_window(rs, window, axis)
298+
rs = _center_window(rs, window, axis, center=center)
291299
# GH2953, fixup edges
292300
if window > 2:
293-
if values.ndim > 1:
294-
# TODO: handle mi vectorized case
295-
pass
301+
302+
# there's an ambiguity on what constitutes
303+
# the "center" when window is even
304+
# we Just close ranks with numpy , see test case
305+
# delta = 1 if window % 2 == 0 else 0
306+
if window % 2 == 0 :
307+
nahead = (window-1)//2 or 1
296308
else:
297-
# there's an ambiguity on what constitutes
298-
# the "center" when window is even
299-
# we Just close ranks with numpy , see test case
300-
# delta = 1 if window % 2 == 0 else 0
301-
if window % 2 == 0 :
302-
nahead = (window-1)//2 or 1
303-
else:
304-
nahead = (window)//2
305-
306-
# fixup the head
307-
tip = np.append(np.zeros(nahead+1),values[:(2*nahead+1)])
308-
rs[:nahead+1] = calc(tip)[-(nahead+1):][:nahead+1]
309-
310-
# fixup the tail
311-
tip = np.append(values[-(2*nahead+1):],np.zeros(nahead))
312-
rs[-(nahead):] = calc(tip)[-(nahead):]
313-
314-
if minp > 0:
315-
d = minp - nahead-1
316-
if d > 0:
317-
rs[:d] = NaN
318-
rs[-(d):] = NaN
309+
nahead = (window)//2
310+
311+
# fixup the head
312+
shape = list(values.shape)
313+
shape[0] = nahead+1
314+
tip = np.append(np.zeros(tuple(shape)),values[:(2*nahead+1)],axis=0)
315+
rs[:nahead+1] = calc(tip)[-(nahead+1):][:nahead+1]
316+
if minp > 0:
317+
d = max(minp,nahead+1)
318+
if d > 0:
319+
rs[:d] = NaN
320+
321+
322+
# fixup the tail
323+
shape = list(values.shape)
324+
shape[0] = nahead
325+
tip = np.append(values[-(2*nahead+1):],np.zeros(tuple(shape)),axis=0)
326+
rs[-(nahead):] = calc(tip)[-(nahead):]
327+
328+
if minp > 0:
329+
d = max(nahead,minp)
330+
if d > 0:
331+
rs[-d:] = NaN
319332

320333
return rs
321334

322335

323-
def _center_window(rs, window, axis):
336+
def _center_window(rs, window, axis, center=None):
324337
offset = int((window - 1) / 2.)
325338
if isinstance(rs, (Series, DataFrame, Panel)):
326339
rs = rs.shift(-offset, axis=axis)
@@ -335,7 +348,23 @@ def _center_window(rs, window, axis):
335348
na_indexer[axis] = slice(-offset, None)
336349

337350
rs[tuple(rs_indexer)] = np.copy(rs[tuple(lead_indexer)])
351+
352+
# center can optionally point to an action on
353+
# lhs or rhs
354+
355+
if not isinstance(center, tuple):
356+
center = ('copy','na')
357+
lhs, rhs = center
358+
359+
# lhs : default is copy
360+
if lhs == 'na':
361+
lhs_indexer = [slice(None)] * rs.ndim
362+
lhs_indexer[axis] = slice(0, offset+1)
363+
rs[tuple(lhs_indexer)] = np.nan
364+
365+
# rhs : default is na
338366
rs[tuple(na_indexer)] = np.nan
367+
339368
return rs
340369

341370

pandas/stats/tests/test_moments.py

+2
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ def _check_structures(self, func, static_comp,
442442
if has_min_periods:
443443
minp = 10
444444
series_xp = func(self.series, 25, min_periods=minp).shift(-12)
445+
series_xp[:13] = np.nan
445446
frame_xp = func(self.frame, 25, min_periods=minp).shift(-12)
447+
frame_xp[:13] = np.nan
446448

447449
series_rs = func(self.series, 25, min_periods=minp,
448450
center=True)

0 commit comments

Comments
 (0)