Skip to content

Commit 05eb20f

Browse files
committed
DEPR: deprecate freq/how arguments to window functions
1 parent 0bbe110 commit 05eb20f

File tree

5 files changed

+95
-62
lines changed

5 files changed

+95
-62
lines changed

doc/source/computation.rst

+6-5
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@ Window Functions
207207
functions and are now deprecated and replaced by the corresponding method call.
208208

209209
The deprecation warning will show the new syntax, see an example :ref:`here <whatsnew_0180.window_deprecations>`
210-
211210
You can view the previous documentation
212211
`here <http://pandas.pydata.org/pandas-docs/version/0.17.1/computation.html#moving-rolling-statistics-moments>`__
213212

@@ -244,8 +243,12 @@ accept the following arguments:
244243
- ``window``: size of moving window
245244
- ``min_periods``: threshold of non-null data points to require (otherwise
246245
result is NA)
247-
- ``freq``: optionally specify a :ref:`frequency string <timeseries.alias>`
248-
or :ref:`DateOffset <timeseries.offsets>` to pre-conform the data to.
246+
247+
.. warning::
248+
249+
The ``freq`` and ``how`` arguments were in the API prior to 0.18.0 changes. These are deprecated in the new API. You can simply resample the input prior to creating a window function.
250+
251+
For example, instead of ``s.rolling(window=5,freq='D').max()`` to get the max value on a rolling 5 Day window, one could use ``s.resample('D',how='max').rolling(window=5).max()``, which first resamples the data to daily data, then provides a rolling 5 day window.
249252

250253
We can then call methods on these ``rolling`` objects. These return like-indexed objects:
251254

@@ -604,8 +607,6 @@ all accept are:
604607
- ``min_periods``: threshold of non-null data points to require. Defaults to
605608
minimum needed to compute statistic. No ``NaNs`` will be output once
606609
``min_periods`` non-null data points have been seen.
607-
- ``freq``: optionally specify a :ref:`frequency string <timeseries.alias>`
608-
or :ref:`DateOffset <timeseries.offsets>` to pre-conform the data to.
609610

610611
.. note::
611612

doc/source/whatsnew/v0.18.0.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ users upgrade to this version.
1313

1414
Highlights include:
1515

16+
- Window functions are now methods on ``.groupby`` like objects, see :ref:`here <whatsnew_0180.moments>`.
17+
1618
Check the :ref:`API Changes <whatsnew_0180.api>` and :ref:`deprecations <whatsnew_0180.deprecations>` before updating.
1719

1820
.. contents:: What's new in v0.18.0
@@ -212,7 +214,7 @@ Deprecations
212214

213215
.. _whatsnew_0180.window_deprecations:
214216

215-
- Function ``pd.rolling_*``, ``pd.expanding_*``, and ``pd.ewm*`` are deprecated and replaced by the corresponding method call. Note that
217+
- The functions ``pd.rolling_*``, ``pd.expanding_*``, and ``pd.ewm*`` are deprecated and replaced by the corresponding method call. Note that
216218
the new suggested syntax includes all of the arguments (even if default) (:issue:`11603`)
217219

218220
.. code-block:: python
@@ -237,6 +239,8 @@ Deprecations
237239
2 0.5
238240
dtype: float64
239241

242+
- The the ``freq`` and ``how`` arguments to the ``.rolling``, ``.expanding``, and ``.ewm`` (new) functions are deprecated, and will be removed in a future version. (:issue:`11603`)
243+
240244
.. _whatsnew_0180.prior_deprecations:
241245

242246
Removal of prior version deprecations/changes

pandas/core/base.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,8 @@ def _aggregate_multiple_funcs(self, arg, _level):
462462
colg = self._gotitem(obj.name, ndim=1, subset=obj)
463463
results.append(colg.aggregate(a))
464464

465-
# find a good name, this could be a function that we don't recognize
466-
name = self._is_cython_func(a) or a
467-
if not isinstance(name, compat.string_types):
468-
name = getattr(a,'name',a)
469-
if not isinstance(name, compat.string_types):
470-
name = getattr(a,'__name__',a)
471-
465+
# make sure we find a good name
466+
name = com._get_callable_name(a) or a
472467
keys.append(name)
473468
except (TypeError, DataError):
474469
pass

pandas/core/window.py

+49-27
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88
from __future__ import division
99

10+
import warnings
1011
import numpy as np
1112
from functools import wraps
1213
from collections import defaultdict
@@ -39,6 +40,12 @@ class _Window(PandasObject, SelectionMixin):
3940

4041
def __init__(self, obj, window=None, min_periods=None, freq=None, center=False,
4142
win_type=None, axis=0):
43+
44+
if freq is not None:
45+
warnings.warn("The freq kw is deprecated and will be removed in a future version. You can resample prior "
46+
"to passing to a window function",
47+
FutureWarning, stacklevel=3)
48+
4249
self.blocks = []
4350
self.obj = obj
4451
self.window = window
@@ -298,7 +305,7 @@ def _apply_window(self, mean=True, how=None, **kwargs):
298305
----------
299306
mean : boolean, default True
300307
If True computes weighted mean, else weighted sum
301-
how : string, default to None
308+
how : string, default to None (DEPRECATED)
302309
how to resample
303310
304311
Returns
@@ -378,7 +385,7 @@ def _apply(self, func, window=None, center=None, check_minp=None, how=None, **kw
378385
window : int/array, default to _get_window()
379386
center : boolean, default to self.center
380387
check_minp : function, default to _use_window
381-
how : string, default to None
388+
how : string, default to None (DEPRECATED)
382389
how to resample
383390
384391
Returns
@@ -486,21 +493,31 @@ def sum(self):
486493
487494
Parameters
488495
----------
489-
how : string, default max
496+
how : string, default 'max' (DEPRECATED)
490497
Method for down- or re-sampling""")
491-
492-
def max(self, how='max'):
498+
def max(self, how=None):
499+
if how is not None:
500+
warnings.warn("The how kw argument is deprecated and removed in a future version. You can resample prior "
501+
"to passing to a window function",
502+
FutureWarning, stacklevel=3)
503+
else:
504+
how = 'max'
493505
return self._apply('roll_max', how=how)
494506

495507
_shared_docs['min'] = dedent("""
496508
%(name)s minimum
497509
498510
Parameters
499511
----------
500-
how : string, default min
512+
how : string, default 'min' (DEPRECATED)
501513
Method for down- or re-sampling""")
502-
503-
def min(self, how='min'):
514+
def min(self, how=None):
515+
if how is not None:
516+
warnings.warn("The how kw argument is deprecated and removed in a future version. You can resample prior "
517+
"to passing to a window function",
518+
FutureWarning, stacklevel=3)
519+
else:
520+
how = 'min'
504521
return self._apply('roll_min', how=how)
505522

506523
_shared_docs['mean'] = """%(name)s mean"""
@@ -512,10 +529,15 @@ def mean(self):
512529
513530
Parameters
514531
----------
515-
how : string, default median
532+
how : string, default 'median' (DEPRECATED)
516533
Method for down- or re-sampling""")
517-
518-
def median(self, how='median'):
534+
def median(self, how=None):
535+
if how is not None:
536+
warnings.warn("The how kw argument is deprecated and removed in a future version. You can resample prior "
537+
"to passing to a window function",
538+
FutureWarning, stacklevel=3)
539+
else:
540+
how = 'median'
519541
return self._apply('roll_median_c', how=how)
520542

521543
_shared_docs['std'] = dedent("""
@@ -654,7 +676,7 @@ class Rolling(_Rolling_and_Expanding):
654676
min_periods : int, default None
655677
Minimum number of observations in window required to have a value
656678
(otherwise result is NA).
657-
freq : string or DateOffset object, optional (default None)
679+
freq : string or DateOffset object, optional (default None) (DEPRECATED)
658680
Frequency to conform the data to before computing the statistic. Specified
659681
as a frequency string or DateOffset object.
660682
center : boolean, default False
@@ -704,14 +726,14 @@ def sum(self):
704726
@Substitution(name='rolling')
705727
@Appender(_doc_template)
706728
@Appender(_shared_docs['max'])
707-
def max(self, how='max'):
708-
return super(Rolling, self).max(how=how)
729+
def max(self, **kwargs):
730+
return super(Rolling, self).max(**kwargs)
709731

710732
@Substitution(name='rolling')
711733
@Appender(_doc_template)
712734
@Appender(_shared_docs['min'])
713-
def min(self, how='min'):
714-
return super(Rolling, self).min(how=how)
735+
def min(self, **kwargs):
736+
return super(Rolling, self).min(**kwargs)
715737

716738
@Substitution(name='rolling')
717739
@Appender(_doc_template)
@@ -722,8 +744,8 @@ def mean(self):
722744
@Substitution(name='rolling')
723745
@Appender(_doc_template)
724746
@Appender(_shared_docs['median'])
725-
def median(self, how='median'):
726-
return super(Rolling, self).median(how=how)
747+
def median(self, **kwargs):
748+
return super(Rolling, self).median(**kwargs)
727749

728750
@Substitution(name='rolling')
729751
@Appender(_doc_template)
@@ -778,7 +800,7 @@ class Expanding(_Rolling_and_Expanding):
778800
min_periods : int, default None
779801
Minimum number of observations in window required to have a value
780802
(otherwise result is NA).
781-
freq : string or DateOffset object, optional (default None)
803+
freq : string or DateOffset object, optional (default None) (DEPRECATED)
782804
Frequency to conform the data to before computing the statistic. Specified
783805
as a frequency string or DateOffset object.
784806
center : boolean, default False
@@ -843,14 +865,14 @@ def sum(self):
843865
@Substitution(name='expanding')
844866
@Appender(_doc_template)
845867
@Appender(_shared_docs['max'])
846-
def max(self, how='max'):
847-
return super(Expanding, self).max(how=how)
868+
def max(self, **kwargs):
869+
return super(Expanding, self).max(**kwargs)
848870

849871
@Substitution(name='expanding')
850872
@Appender(_doc_template)
851873
@Appender(_shared_docs['min'])
852-
def min(self, how='min'):
853-
return super(Expanding, self).min(how=how)
874+
def min(self, **kwargs):
875+
return super(Expanding, self).min(**kwargs)
854876

855877
@Substitution(name='expanding')
856878
@Appender(_doc_template)
@@ -861,8 +883,8 @@ def mean(self):
861883
@Substitution(name='expanding')
862884
@Appender(_doc_template)
863885
@Appender(_shared_docs['median'])
864-
def median(self, how='median'):
865-
return super(Expanding, self).median(how=how)
886+
def median(self, **kwargs):
887+
return super(Expanding, self).median(**kwargs)
866888

867889
@Substitution(name='expanding')
868890
@Appender(_doc_template)
@@ -923,7 +945,7 @@ class EWM(_Rolling):
923945
min_periods : int, default 0
924946
Minimum number of observations in window required to have a value
925947
(otherwise result is NA).
926-
freq : None or string alias / date offset object, default=None
948+
freq : None or string alias / date offset object, default=None (DEPRECATED)
927949
Frequency to conform to before computing statistic
928950
adjust : boolean, default True
929951
Divide by decaying adjustment factor in beginning periods to account for
@@ -1004,7 +1026,7 @@ def _apply(self, func, how=None, **kwargs):
10041026
Parameters
10051027
----------
10061028
func : string/callable to apply
1007-
how : string, default to None
1029+
how : string, default to None (DEPRECATED)
10081030
how to resample
10091031
10101032
Returns

pandas/tests/test_window.py

+33-22
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ def b(x):
191191
result = r.aggregate([a,b])
192192
assert_frame_equal(result, expected)
193193

194+
def test_preserve_metadata(self):
195+
# GH 10565
196+
s = Series(np.arange(100), name='foo')
197+
198+
s2 = s.rolling(30).sum()
199+
s3 = s.rolling(20).sum()
200+
self.assertEqual(s2.name, 'foo')
201+
self.assertEqual(s3.name, 'foo')
202+
194203
class TestDeprecations(Base):
195204
""" test that we are catching deprecation warnings """
196205

@@ -815,10 +824,15 @@ def get_result(obj, window, min_periods=None, freq=None, center=False):
815824

816825
# check via the API calls if name is provided
817826
if name is not None:
818-
return getattr(obj.rolling(window=window,
819-
min_periods=min_periods,
820-
freq=freq,
821-
center=center),name)(**kwargs)
827+
828+
# catch a freq deprecation warning if freq is provided and not None
829+
w = FutureWarning if freq is not None else None
830+
with tm.assert_produces_warning(w, check_stacklevel=False):
831+
r = obj.rolling(window=window,
832+
min_periods=min_periods,
833+
freq=freq,
834+
center=center)
835+
return getattr(r,name)(**kwargs)
822836

823837
# check via the moments API
824838
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
@@ -1002,15 +1016,6 @@ def test_ewma_halflife_arg(self):
10021016
self.assertRaises(Exception, mom.ewma, self.arr, com=9.5, span=20, halflife=50)
10031017
self.assertRaises(Exception, mom.ewma, self.arr)
10041018

1005-
def test_moment_preserve_series_name(self):
1006-
# GH 10565
1007-
s = Series(np.arange(100), name='foo')
1008-
1009-
s2 = s.rolling(30).sum()
1010-
s3 = s.rolling(20).sum()
1011-
self.assertEqual(s2.name, 'foo')
1012-
self.assertEqual(s3.name, 'foo')
1013-
10141019
def test_ew_empty_arrays(self):
10151020
arr = np.array([], dtype=np.float64)
10161021

@@ -2133,7 +2138,8 @@ def test_rolling_max_gh6297(self):
21332138
expected = Series([1.0, 2.0, 6.0, 4.0, 5.0],
21342139
index=[datetime(1975, 1, i, 0)
21352140
for i in range(1, 6)])
2136-
x = series.rolling(window=1, freq='D').max()
2141+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
2142+
x = series.rolling(window=1, freq='D').max()
21372143
assert_series_equal(expected, x)
21382144

21392145
def test_rolling_max_how_resample(self):
@@ -2152,22 +2158,25 @@ def test_rolling_max_how_resample(self):
21522158
expected = Series([0.0, 1.0, 2.0, 3.0, 20.0],
21532159
index=[datetime(1975, 1, i, 0)
21542160
for i in range(1, 6)])
2155-
x = series.rolling(window=1, freq='D').max()
2161+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
2162+
x = series.rolling(window=1, freq='D').max()
21562163
assert_series_equal(expected, x)
21572164

21582165
# Now specify median (10.0)
21592166
expected = Series([0.0, 1.0, 2.0, 3.0, 10.0],
21602167
index=[datetime(1975, 1, i, 0)
21612168
for i in range(1, 6)])
2162-
x = series.rolling(window=1, freq='D').max(how='median')
2169+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
2170+
x = series.rolling(window=1, freq='D').max(how='median')
21632171
assert_series_equal(expected, x)
21642172

21652173
# Now specify mean (4+10+20)/3
21662174
v = (4.0+10.0+20.0)/3.0
21672175
expected = Series([0.0, 1.0, 2.0, 3.0, v],
21682176
index=[datetime(1975, 1, i, 0)
21692177
for i in range(1, 6)])
2170-
x = series.rolling(window=1, freq='D').max(how='mean')
2178+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
2179+
x = series.rolling(window=1, freq='D').max(how='mean')
21712180
assert_series_equal(expected, x)
21722181

21732182

@@ -2187,8 +2196,9 @@ def test_rolling_min_how_resample(self):
21872196
expected = Series([0.0, 1.0, 2.0, 3.0, 4.0],
21882197
index=[datetime(1975, 1, i, 0)
21892198
for i in range(1, 6)])
2190-
x = series.rolling(window=1, freq='D').min()
2191-
assert_series_equal(expected, x)
2199+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
2200+
r = series.rolling(window=1, freq='D')
2201+
assert_series_equal(expected, r.min())
21922202

21932203
def test_rolling_median_how_resample(self):
21942204

@@ -2206,14 +2216,15 @@ def test_rolling_median_how_resample(self):
22062216
expected = Series([0.0, 1.0, 2.0, 3.0, 10],
22072217
index=[datetime(1975, 1, i, 0)
22082218
for i in range(1, 6)])
2209-
x = series.rolling(window=1, freq='D').median()
2219+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
2220+
x = series.rolling(window=1, freq='D').median()
22102221
assert_series_equal(expected, x)
22112222

22122223
def test_rolling_median_memory_error(self):
22132224
# GH11722
22142225
n = 20000
2215-
mom.rolling_median(Series(np.random.randn(n)), window=2, center=False)
2216-
mom.rolling_median(Series(np.random.randn(n)), window=2, center=False)
2226+
Series(np.random.randn(n)).rolling(window=2, center=False).median()
2227+
Series(np.random.randn(n)).rolling(window=2, center=False).median()
22172228

22182229
if __name__ == '__main__':
22192230
import nose

0 commit comments

Comments
 (0)