From 09084bbbc34462eeb052332aeb411b104f469103 Mon Sep 17 00:00:00 2001 From: Chang She Date: Thu, 26 Apr 2012 17:35:34 -0400 Subject: [PATCH 1/5] ENH: allows Index - timedelta to make it symmetric to Index + timedelta --- pandas/core/index.py | 5 ++++- pandas/tests/test_index.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pandas/core/index.py b/pandas/core/index.py index b8efc5cfe0a3e..b789f4b7bfde6 100644 --- a/pandas/core/index.py +++ b/pandas/core/index.py @@ -436,7 +436,10 @@ def __add__(self, other): __ge__ = _indexOp('__ge__') def __sub__(self, other): - return self.diff(other) + if isinstance(other, Index): + return self.diff(other) + else: + return Index(self.view(np.ndarray) - other) def __and__(self, other): return self.intersection(other) diff --git a/pandas/tests/test_index.py b/pandas/tests/test_index.py index d2ef630d09417..9aaa8dcf5e025 100644 --- a/pandas/tests/test_index.py +++ b/pandas/tests/test_index.py @@ -221,8 +221,20 @@ def test_add(self): tm.assert_contains_all(self.strIndex, secondCat) tm.assert_contains_all(self.dateIndex, firstCat) + def test_timedelta(self): # this is valid too shifted = self.dateIndex + timedelta(1) + back = shifted + timedelta(-1) + self.assert_(tm.equalContents(self.dateIndex, back)) + self.assertEqual(shifted.freq, self.dateIndex.freq) + self.assertEqual(shifted.freq, back.freq) + + lead = self.dateIndex - timedelta(1) + back = lead - timedelta(-1) + self.assert_(tm.equalContents(self.dateIndex, back)) + self.assertEqual(self.dateIndex.freq, back.freq) + self.assertEqual(lead.freq, back.freq) + def test_append_multiple(self): index = Index(['a', 'b', 'c', 'd', 'e', 'f']) From 785537e10e381d3b789f721288226e0fad177d95 Mon Sep 17 00:00:00 2001 From: Chang She Date: Thu, 26 Apr 2012 17:42:53 -0400 Subject: [PATCH 2/5] BUG: keep frequency information when adding timedeltas ENH: allow DatetimeIndex - timedelta --- pandas/tseries/index.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 1cae0a8e91fd9..bc11a69cbfdd2 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -150,9 +150,6 @@ class DatetimeIndex(Int64Index): __le__ = _dt_index_cmp('__le__') __ge__ = _dt_index_cmp('__ge__') - __add__ = _dt_index_op('__add__') - __sub__ = _dt_index_op('__sub__') - # structured array cache for datetime fields _sarr_cache = None @@ -420,7 +417,16 @@ def __add__(self, other): return self.union(other) elif isinstance(other, (datetools.DateOffset, timedelta)): new_values = self.astype('O') + other - return DatetimeIndex(new_values, tz=self.tz) + return DatetimeIndex(new_values, tz=self.tz, freq=self.freq) + else: + return Index(self.view(np.ndarray) + other) + + def __sub__(self, other): + if isinstance(other, Index): + return self.diff(other) + elif isinstance(other, (datetools.DateOffset, timedelta)): + new_values = self.astype('O') - other + return DatetimeIndex(new_values, tz=self.tz, freq=self.freq) else: return Index(self.view(np.ndarray) + other) From 6b48af3d4100fa8823e03de4fbc279d7b86aa699 Mon Sep 17 00:00:00 2001 From: Chang She Date: Thu, 26 Apr 2012 17:46:05 -0400 Subject: [PATCH 3/5] Removed old duplicated makeDateIndex definition --- pandas/util/testing.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 50a3bdd6577f9..9e4475cb62339 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -163,10 +163,6 @@ def makeFloatIndex(k): values = sorted(np.random.random_sample(k)) - np.random.random_sample(1) return Index(values * (10 ** np.random.randint(0, 9))) -def makeDateIndex(k): - dates = list(bdate_range(datetime(2000, 1, 1), periods=k)) - return Index(dates) - def makeFloatSeries(): index = makeStringIndex(N) return Series(randn(N), index=index) From d21725c363b1c944a56014ee45dbabd1f9b50034 Mon Sep 17 00:00:00 2001 From: Chang She Date: Thu, 26 Apr 2012 18:29:58 -0400 Subject: [PATCH 4/5] ENH loffset in resample. GH #1127 --- pandas/core/generic.py | 10 +++++++++- pandas/core/index.py | 5 +---- pandas/tests/test_index.py | 7 ------- pandas/tseries/index.py | 11 +---------- pandas/tseries/tests/test_resample.py | 24 ++++++++++++++++++++++-- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 7705cbd9bfd11..86f783c83901f 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1,10 +1,12 @@ # pylint: disable=W0231 +from datetime import timedelta import numpy as np from pandas.core.common import save, load from pandas.core.index import MultiIndex from pandas.tseries.index import DatetimeIndex +from pandas.tseries.offsets import DateOffset #------------------------------------------------------------------------------- # Picklable mixin @@ -136,7 +138,8 @@ def groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, sort=sort, group_keys=group_keys) def resample(self, rule, how='mean', axis=0, as_index=True, - fill_method=None, closed='right', label='right', kind=None): + fill_method=None, closed='right', label='right', kind=None, + loffset=None): """ Convenience method for frequency conversion and resampling of regular time-series data. @@ -152,6 +155,8 @@ def resample(self, rule, how='mean', axis=0, as_index=True, label : {'right', 'left'}, default 'right' Which bin edge label to label bucket with as_index : see synonymous argument of groupby + loffset : timedelta + Adjust the resampled time labels """ from pandas.tseries.resample import TimeGrouper @@ -171,6 +176,9 @@ def resample(self, rule, how='mean', axis=0, as_index=True, # upsampling result = self.reindex(grouper.binner[1:], method=fill_method) + if isinstance(loffset, (DateOffset, timedelta)): + if len(result.index) > 0: + result.index = result.index + loffset return result def first(self, offset): diff --git a/pandas/core/index.py b/pandas/core/index.py index b789f4b7bfde6..b8efc5cfe0a3e 100644 --- a/pandas/core/index.py +++ b/pandas/core/index.py @@ -436,10 +436,7 @@ def __add__(self, other): __ge__ = _indexOp('__ge__') def __sub__(self, other): - if isinstance(other, Index): - return self.diff(other) - else: - return Index(self.view(np.ndarray) - other) + return self.diff(other) def __and__(self, other): return self.intersection(other) diff --git a/pandas/tests/test_index.py b/pandas/tests/test_index.py index 9aaa8dcf5e025..86af120b7af4e 100644 --- a/pandas/tests/test_index.py +++ b/pandas/tests/test_index.py @@ -229,13 +229,6 @@ def test_timedelta(self): self.assertEqual(shifted.freq, self.dateIndex.freq) self.assertEqual(shifted.freq, back.freq) - lead = self.dateIndex - timedelta(1) - back = lead - timedelta(-1) - self.assert_(tm.equalContents(self.dateIndex, back)) - self.assertEqual(self.dateIndex.freq, back.freq) - self.assertEqual(lead.freq, back.freq) - - def test_append_multiple(self): index = Index(['a', 'b', 'c', 'd', 'e', 'f']) diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index bc11a69cbfdd2..1e7cadf2dbfe2 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -417,16 +417,7 @@ def __add__(self, other): return self.union(other) elif isinstance(other, (datetools.DateOffset, timedelta)): new_values = self.astype('O') + other - return DatetimeIndex(new_values, tz=self.tz, freq=self.freq) - else: - return Index(self.view(np.ndarray) + other) - - def __sub__(self, other): - if isinstance(other, Index): - return self.diff(other) - elif isinstance(other, (datetools.DateOffset, timedelta)): - new_values = self.astype('O') - other - return DatetimeIndex(new_values, tz=self.tz, freq=self.freq) + return DatetimeIndex(new_values, tz=self.tz) else: return Index(self.view(np.ndarray) + other) diff --git a/pandas/tseries/tests/test_resample.py b/pandas/tseries/tests/test_resample.py index c00cf242e39d2..2451bbeeddeb7 100644 --- a/pandas/tseries/tests/test_resample.py +++ b/pandas/tseries/tests/test_resample.py @@ -1,11 +1,11 @@ -from datetime import datetime +from datetime import datetime, timedelta import numpy as np from pandas import Series, DataFrame, isnull, notnull from pandas.tseries.index import date_range -from pandas.tseries.offsets import Minute +from pandas.tseries.offsets import Minute, bday from pandas.tseries.period import period_range from pandas.tseries.resample import DatetimeIndex, TimeGrouper import pandas.tseries.offsets as offsets @@ -133,6 +133,26 @@ def test_resample_basic(self): self.assertEquals(result.irow(1), s['1/4/2005']) self.assertEquals(result.irow(5), s['1/10/2005']) + def test_resample_loffset(self): + rng = date_range('1/1/2000 00:00:00', '1/1/2000 00:13:00', freq='min') + s = Series(np.random.randn(14), index=rng) + result = s.resample('5min', how='mean', closed='right', label='right', + loffset=timedelta(minutes=1)) + idx = date_range('1/1/2000', periods=4, freq='5min') + expected = Series([s[0], s[1:6].mean(), s[6:11].mean(), s[11:].mean()], + index=idx + timedelta(minutes=1)) + assert_series_equal(result, expected) + + # from daily + dti = DatetimeIndex(start=datetime(2005,1,1), end=datetime(2005,1,10), + freq='D') + ser = Series(np.random.rand(len(dti)), dti) + + # to weekly + result = ser.resample('w-sun', how='last') + expected = ser.resample('w-sun', how='last', loffset=-bday) + self.assertEqual(result.index[0] - bday, expected.index[0]) + def test_resample_upsample(self): # from daily dti = DatetimeIndex(start=datetime(2005,1,1), end=datetime(2005,1,10), From e08f299672bf39849ab386d7b2a5664a100fc449 Mon Sep 17 00:00:00 2001 From: Chang She Date: Thu, 26 Apr 2012 18:37:17 -0400 Subject: [PATCH 5/5] Accidentally removed__sub__ --- pandas/tseries/index.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 1e7cadf2dbfe2..cdc031a3b4d76 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -150,6 +150,8 @@ class DatetimeIndex(Int64Index): __le__ = _dt_index_cmp('__le__') __ge__ = _dt_index_cmp('__ge__') + __sub__ = _dt_index_op('__sub__') + # structured array cache for datetime fields _sarr_cache = None