diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 0873e4b34b0b1..41d5090ef6c32 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -321,3 +321,4 @@ Bug Fixes - Require at least 0.23 version of cython to avoid problems with character encodings (:issue:`14699`) - Bug in converting object elements of array-like objects to unsigned 64-bit integers (:issue:`4471`) - Bug in ``pd.pivot_table()`` where no error was raised when values argument was not in the columns (:issue:`14938`) +- Bug in ``resample``, where a non-string ```loffset`` argument would not be applied when resampling a timeseries (:issue:`13218`) diff --git a/pandas/tseries/resample.py b/pandas/tseries/resample.py index 31781eb3fc131..cf96a688fb21f 100755 --- a/pandas/tseries/resample.py +++ b/pandas/tseries/resample.py @@ -323,6 +323,11 @@ def aggregate(self, arg, *args, **kwargs): *args, **kwargs) + # if arg was a string, _aggregate called resampler's _downsample or + # _groupby_and_agg methods, which would've already applied the loffset + if not isinstance(arg, compat.string_types): + result = self._apply_loffset(result) + return result agg = aggregate @@ -381,7 +386,7 @@ def _gotitem(self, key, ndim, subset=None): return grouped def _groupby_and_aggregate(self, how, grouper=None, *args, **kwargs): - """ revaluate the obj with a groupby aggregation """ + """ re-evaluate the obj with a groupby aggregation """ if grouper is None: self._set_binner() @@ -409,7 +414,14 @@ def _groupby_and_aggregate(self, how, grouper=None, *args, **kwargs): return self._wrap_result(result) def _apply_loffset(self, result): - """if loffset if set, offset the result index""" + """ + if loffset is set, offset the result index + + Parameters + ---------- + result : Series or DataFrame + the result of resample + """ loffset = self.loffset if isinstance(loffset, compat.string_types): loffset = to_offset(self.loffset) @@ -419,6 +431,7 @@ def _apply_loffset(self, result): isinstance(result.index, DatetimeIndex) and len(result.index) > 0 ) + if needs_offset: result.index = result.index + loffset @@ -797,6 +810,11 @@ def aggregate(self, arg, *args, **kwargs): if result is None: result = self._downsample(arg, *args, **kwargs) + # if arg was a string, _aggregate called resamplers' _downsample or + # _groupby_and_agg methods, which would've already applied the loffset + if not isinstance(arg, compat.string_types): + result = self._apply_loffset(result) + return result agg = aggregate diff --git a/pandas/tseries/tests/test_resample.py b/pandas/tseries/tests/test_resample.py index b8c060c024867..8f1c653210298 100755 --- a/pandas/tseries/tests/test_resample.py +++ b/pandas/tseries/tests/test_resample.py @@ -24,7 +24,7 @@ from pandas.tseries.period import period_range, PeriodIndex, Period from pandas.tseries.resample import (DatetimeIndex, TimeGrouper, DatetimeIndexResampler) -from pandas.tseries.tdi import timedelta_range +from pandas.tseries.tdi import timedelta_range, TimedeltaIndex from pandas.util.testing import (assert_series_equal, assert_almost_equal, assert_frame_equal, assert_index_equal) from pandas._period import IncompatibleFrequency @@ -769,6 +769,36 @@ def test_resample_empty_dtypes(self): # (ex: doing mean with dtype of np.object) pass + def test_resample_loffset_arg_type(self): + # GH 13218, 15002 + df = self.create_series().to_frame('value') + expected_means = [df.values[i:i + 2].mean() + for i in range(0, len(df.values), 2)] + expected_index = self.create_index(df.index[0], + periods=len(df.index) / 2, + freq='2D') + # loffset coreces PeriodIndex to DateTimeIndex + if isinstance(expected_index, PeriodIndex): + expected_index = expected_index.to_timestamp() + expected_index += timedelta(hours=2) + expected = DataFrame({'value': expected_means}, index=expected_index) + for arg in ['mean', {'value': 'mean'}, ['mean']]: + result_agg = df.resample('2D', loffset='2H').agg(arg) + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False): + result_how = df.resample('2D', how=arg, loffset='2H') + if isinstance(arg, list): + expected.columns = pd.MultiIndex.from_tuples([('value', + 'mean')]) + # GH 13022, 7687 - TODO: fix resample w/ TimedeltaIndex + if isinstance(expected.index, TimedeltaIndex): + with tm.assertRaises(AssertionError): + assert_frame_equal(result_agg, expected) + assert_frame_equal(result_how, expected) + else: + assert_frame_equal(result_agg, expected) + assert_frame_equal(result_how, expected) + class TestDatetimeIndex(Base, tm.TestCase): _multiprocess_can_split_ = True