Skip to content

Commit d9d59f6

Browse files
committed
tweaks
1 parent 915d261 commit d9d59f6

File tree

2 files changed

+146
-13
lines changed

2 files changed

+146
-13
lines changed

pandas/stats/moments.py

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
'rolling_sum', 'rolling_mean', 'rolling_std', 'rolling_cov',
1919
'rolling_corr', 'rolling_var', 'rolling_skew', 'rolling_kurt',
2020
'rolling_quantile', 'rolling_median', 'rolling_apply',
21-
'rolling_corr_pairwise',
21+
'rolling_corr_pairwise', 'rolling_window',
2222
'ewma', 'ewmvar', 'ewmstd', 'ewmvol', 'ewmcorr', 'ewmcov',
2323
'expanding_count', 'expanding_max', 'expanding_min',
2424
'expanding_sum', 'expanding_mean', 'expanding_std',
@@ -285,11 +285,10 @@ def _rolling_moment(arg, window, func, minp, axis=0, freq=None,
285285
return rs
286286

287287
def _center_window(rs, window, axis):
288+
offset = int((window - 1) / 2.)
288289
if isinstance(rs, (Series, DataFrame, Panel)):
289-
rs = rs.shift(-int((window + 1) / 2.), axis=axis)
290+
rs = rs.shift(-offset, axis=axis)
290291
else:
291-
offset = int((window + 1) / 2.)
292-
293292
rs_indexer = [slice(None)] * rs.ndim
294293
rs_indexer[axis] = slice(None, -offset)
295294

@@ -573,6 +572,71 @@ def call_cython(arg, window, minp):
573572
return _rolling_moment(arg, window, call_cython, min_periods,
574573
freq=freq, center=center, time_rule=time_rule)
575574

575+
def rolling_window(arg, window, window_type='boxcar', min_periods=None,
576+
freq=None, center=False, time_rule=None, **kwargs):
577+
"""
578+
Applies a centered moving window of type ``window_type`` and size ``window``
579+
on the data.
580+
581+
Parameters
582+
----------
583+
data : Series, DataFrame
584+
window : int
585+
Size of the filtering window.
586+
window_type : str, default 'boxcar'
587+
Window type (see Notes)
588+
589+
min_periods : int
590+
Minimum number of observations in window required to have a value
591+
freq : None or string alias / date offset object, default=None
592+
Frequency to conform to before computing statistic
593+
center : boolean, default False
594+
Whether the label should correspond with center of window
595+
596+
Returns
597+
-------
598+
y : type of input argument
599+
600+
Notes
601+
-----
602+
The recognized window types are:
603+
604+
* ``boxcar``
605+
* ``triang``
606+
* ``blackman``
607+
* ``hamming``
608+
* ``bartlett``
609+
* ``parzen``
610+
* ``bohman``
611+
* ``blackmanharris``
612+
* ``nuttall``
613+
* ``barthann``
614+
* ``kaiser`` (needs beta)
615+
* ``gaussian`` (needs std)
616+
* ``general_gaussian`` (needs power, width)
617+
* ``slepian`` (needs width).
618+
"""
619+
from scipy.signal import convolve, get_window
620+
621+
data = marray(data, copy=True, subok=True)
622+
if data._mask is nomask:
623+
data._mask = np.zeros(data.shape, bool_)
624+
window = get_window(window_type, span, fftbins=False)
625+
(n, k) = (len(data), span//2)
626+
#
627+
if data.ndim == 1:
628+
data._data.flat = convolve(data._data, window)[k:n+k] / float(span)
629+
data._mask[:] = ((convolve(getmaskarray(data), window) > 0)[k:n+k])
630+
elif data.ndim == 2:
631+
for i in range(data.shape[-1]):
632+
_data = data._data[:,i]
633+
_data.flat = convolve(_data, window)[k:n+k] / float(span)
634+
data._mask[:,i] = (convolve(data._mask[:,i], window) > 0)[k:n+k]
635+
else:
636+
raise ValueError, "Data should be at most 2D"
637+
data._mask[:k] = data._mask[-k:] = True
638+
return data
639+
576640

577641
def _expanding_func(func, desc, check_minp=_use_window):
578642
@Substitution(desc, _unary_arg, _type_of_input)

pandas/stats/tests/test_moments.py

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,75 @@ def test_rolling_count(self):
4848
def test_rolling_mean(self):
4949
self._check_moment_func(mom.rolling_mean, np.mean)
5050

51+
def test_cmov_mean(self):
52+
try:
53+
from scikits.timeseries.lib import cmov_mean
54+
except ImportError:
55+
raise nose.SkipTest
56+
57+
vals = np.random.randn(10)
58+
xp = cmov_mean(vals, 5)
59+
60+
rs = mom.rolling_mean(vals, 5, center=True)
61+
assert_almost_equal(xp.compressed(), rs[2:-2])
62+
assert_almost_equal(xp.mask, np.isnan(rs))
63+
64+
xp = Series(rs)
65+
rs = mom.rolling_mean(Series(vals), 5, center=True)
66+
assert_series_equal(xp, rs)
67+
68+
def test_cmov_window(self):
69+
try:
70+
from scikits.timeseries.lib import cmov_window
71+
except ImportError:
72+
raise nose.SkipTest
73+
74+
vals = np.random.randn(10)
75+
xp = cmov_window(vals, 5, 'boxcar')
76+
77+
rs = mom.rolling_window(vals, 5, center=True)
78+
assert_almost_equal(xp.compressed(), rs[2:-2])
79+
assert_almost_equal(xp.mask, np.isnan(rs))
80+
81+
xp = Series(rs)
82+
rs = mom.rolling_window(Series(vals), 5, center=True)
83+
assert_series_equal(xp, rs)
84+
85+
def test_cmov_window_types(self):
86+
try:
87+
from scikits.timeseries.lib import cmov_window
88+
except ImportError:
89+
raise nose.SkipTest
90+
91+
win_types = ['triang', 'blackman', 'hamming', 'bartlett', 'bohman',
92+
'blackmanharris', 'nuttall', 'barthann']
93+
for wt in win_types:
94+
vals = np.random.randn(10)
95+
xp = cmov_window(vals, 5, wt)
96+
97+
rs = mom.rolling_window(vals, 5, window_type=wt, center=True)
98+
assert_almost_equal(xp.compressed(), rs[2:-2])
99+
assert_almost_equal(xp.mask, np.isnan(rs))
100+
101+
def test_cmov_special(self):
102+
try:
103+
from scikits.timeseries.lib import cmov_window
104+
except ImportError:
105+
raise nose.SkipTest
106+
107+
win_types = ['kaiser', 'gaussian', 'general_gaussian', 'slepian']
108+
kwds = [{'beta' : 1.}, {'std' : 1.}, {'power' : 2., 'width' : 2.},
109+
{'width' : 2.}]
110+
111+
for wt, k in zip(win_types, kwds):
112+
vals = np.random.randn(10)
113+
xp = cmov_window(vals, 5, (wt,) + tuple(k.values()))
114+
115+
rs = mom.rolling_window(vals, 5, window_type=wt, center=True,
116+
**k)
117+
assert_almost_equal(xp.compressed(), rs[2:-2])
118+
assert_almost_equal(xp.mask, np.isnan(rs))
119+
51120
def test_rolling_median(self):
52121
self._check_moment_func(mom.rolling_median, np.median)
53122

@@ -259,16 +328,16 @@ def _check_ndarray(self, func, static_comp, window=50,
259328
result = func(arr, 20, center=True)
260329
expected = func(arr, 20)
261330

262-
assert_almost_equal(result[0], expected[10])
331+
assert_almost_equal(result[1], expected[10])
263332
if fill_value is None:
264-
self.assert_(np.isnan(result[-10:]).all())
333+
self.assert_(np.isnan(result[-9:]).all())
265334
else:
266-
self.assert_((result[-10:] == 0).all())
335+
self.assert_((result[-9:] == 0).all())
267336
if has_min_periods:
268337
self.assert_(np.isnan(expected[23]))
269-
self.assert_(np.isnan(result[13]))
338+
self.assert_(np.isnan(result[14]))
270339
self.assert_(np.isnan(expected[-5]))
271-
self.assert_(np.isnan(result[-15]))
340+
self.assert_(np.isnan(result[-14]))
272341

273342
def _check_structures(self, func, static_comp,
274343
has_min_periods=True, has_time_rule=True,
@@ -309,17 +378,17 @@ def _check_structures(self, func, static_comp,
309378
if has_center:
310379
if has_min_periods:
311380
minp = 10
312-
series_xp = func(self.series, 25, min_periods=minp).shift(-13)
313-
frame_xp = func(self.frame, 25, min_periods=minp).shift(-13)
381+
series_xp = func(self.series, 25, min_periods=minp).shift(-12)
382+
frame_xp = func(self.frame, 25, min_periods=minp).shift(-12)
314383

315384
series_rs = func(self.series, 25, min_periods=minp,
316385
center=True)
317386
frame_rs = func(self.frame, 25, min_periods=minp,
318387
center=True)
319388

320389
else:
321-
series_xp = func(self.series, 25).shift(-13)
322-
frame_xp = func(self.frame, 25).shift(-13)
390+
series_xp = func(self.series, 25).shift(-12)
391+
frame_xp = func(self.frame, 25).shift(-12)
323392

324393
series_rs = func(self.series, 25, center=True)
325394
frame_rs = func(self.frame, 25, center=True)

0 commit comments

Comments
 (0)