Skip to content

Commit c5d15a0

Browse files
committed
Merge pull request #10292 from sinhrks/tdi_freq
BUG: TimedeltaIndex slicing may reset freq
2 parents c0917de + d9afb5a commit c5d15a0

File tree

5 files changed

+90
-47
lines changed

5 files changed

+90
-47
lines changed

doc/source/whatsnew/v0.16.2.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ Bug Fixes
150150

151151
- Bug in ``Series.align`` resets ``name`` when ``fill_value`` is specified (:issue:`10067`)
152152
- Bug in ``SparseSeries.abs`` resets ``name`` (:issue:`10241`)
153-
153+
- Bug in ``TimedeltaIndex`` slicing may reset freq (:issue:`10292`)
154154

155155
- Bug in GroupBy.get_group raises ValueError when group key contains NaT (:issue:`6992`)
156156
- Bug in ``SparseSeries`` constructor ignores input data name (:issue:`10258`)

pandas/tseries/base.py

+29
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,35 @@ def __contains__(self, key):
6969
except (KeyError, TypeError, ValueError):
7070
return False
7171

72+
def __getitem__(self, key):
73+
getitem = self._data.__getitem__
74+
if np.isscalar(key):
75+
val = getitem(key)
76+
return self._box_func(val)
77+
else:
78+
if com.is_bool_indexer(key):
79+
key = np.asarray(key)
80+
if key.all():
81+
key = slice(0, None, None)
82+
else:
83+
key = lib.maybe_booleans_to_slice(key.view(np.uint8))
84+
85+
attribs = self._get_attributes_dict()
86+
87+
freq = None
88+
if isinstance(key, slice):
89+
if self.freq is not None and key.step is not None:
90+
freq = key.step * self.freq
91+
else:
92+
freq = self.freq
93+
attribs['freq'] = freq
94+
95+
result = getitem(key)
96+
if result.ndim > 1:
97+
return result
98+
99+
return self._simple_new(result, **attribs)
100+
72101
@property
73102
def freqstr(self):
74103
""" return the frequency object as a string if its set, otherwise None """

pandas/tseries/index.py

-26
Original file line numberDiff line numberDiff line change
@@ -1349,32 +1349,6 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None):
13491349
else:
13501350
raise
13511351

1352-
def __getitem__(self, key):
1353-
getitem = self._data.__getitem__
1354-
if np.isscalar(key):
1355-
val = getitem(key)
1356-
return Timestamp(val, offset=self.offset, tz=self.tz)
1357-
else:
1358-
if com.is_bool_indexer(key):
1359-
key = np.asarray(key)
1360-
if key.all():
1361-
key = slice(0,None,None)
1362-
else:
1363-
key = lib.maybe_booleans_to_slice(key.view(np.uint8))
1364-
1365-
new_offset = None
1366-
if isinstance(key, slice):
1367-
if self.offset is not None and key.step is not None:
1368-
new_offset = key.step * self.offset
1369-
else:
1370-
new_offset = self.offset
1371-
1372-
result = getitem(key)
1373-
if result.ndim > 1:
1374-
return result
1375-
1376-
return self._simple_new(result, self.name, new_offset, self.tz)
1377-
13781352
# alias to offset
13791353
def _get_freq(self):
13801354
return self.offset

pandas/tseries/tdi.py

+1-20
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def _generate(cls, start, end, periods, name, offset, closed=None):
244244

245245
@property
246246
def _box_func(self):
247-
return lambda x: Timedelta(x,unit='ns')
247+
return lambda x: Timedelta(x, unit='ns')
248248

249249
@classmethod
250250
def _simple_new(cls, values, name=None, freq=None, **kwargs):
@@ -747,25 +747,6 @@ def _partial_td_slice(self, key, freq, use_lhs=True, use_rhs=True):
747747
# try to find a the dates
748748
return (lhs_mask & rhs_mask).nonzero()[0]
749749

750-
def __getitem__(self, key):
751-
getitem = self._data.__getitem__
752-
if np.isscalar(key):
753-
val = getitem(key)
754-
return Timedelta(val)
755-
else:
756-
if com.is_bool_indexer(key):
757-
key = np.asarray(key)
758-
if key.all():
759-
key = slice(0,None,None)
760-
else:
761-
key = lib.maybe_booleans_to_slice(key.view(np.uint8))
762-
763-
result = getitem(key)
764-
if result.ndim > 1:
765-
return result
766-
767-
return self._simple_new(result, self.name)
768-
769750
def searchsorted(self, key, side='left'):
770751
if isinstance(key, (np.ndarray, Index)):
771752
key = np.array(key, dtype=_TD_DTYPE, copy=False)

pandas/tseries/tests/test_base.py

+59
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,38 @@ def test_nonunique_contains(self):
297297
['2015', '2015', '2016'], ['2015', '2015', '2014'])):
298298
tm.assertIn(idx[0], idx)
299299

300+
def test_getitem(self):
301+
idx1 = pd.date_range('2011-01-01', '2011-01-31', freq='D', name='idx')
302+
idx2 = pd.date_range('2011-01-01', '2011-01-31', freq='D', tz='Asia/Tokyo', name='idx')
303+
304+
for idx in [idx1, idx2]:
305+
result = idx[0]
306+
self.assertEqual(result, pd.Timestamp('2011-01-01', tz=idx.tz))
307+
308+
result = idx[0:5]
309+
expected = pd.date_range('2011-01-01', '2011-01-05', freq='D',
310+
tz=idx.tz, name='idx')
311+
self.assert_index_equal(result, expected)
312+
self.assertEqual(result.freq, expected.freq)
313+
314+
result = idx[0:10:2]
315+
expected = pd.date_range('2011-01-01', '2011-01-09', freq='2D',
316+
tz=idx.tz, name='idx')
317+
self.assert_index_equal(result, expected)
318+
self.assertEqual(result.freq, expected.freq)
319+
320+
result = idx[-20:-5:3]
321+
expected = pd.date_range('2011-01-12', '2011-01-25', freq='3D',
322+
tz=idx.tz, name='idx')
323+
self.assert_index_equal(result, expected)
324+
self.assertEqual(result.freq, expected.freq)
325+
326+
result = idx[4::-1]
327+
expected = DatetimeIndex(['2011-01-05', '2011-01-04', '2011-01-03',
328+
'2011-01-02', '2011-01-01'],
329+
freq='-1D', tz=idx.tz, name='idx')
330+
self.assert_index_equal(result, expected)
331+
self.assertEqual(result.freq, expected.freq)
300332

301333
class TestTimedeltaIndexOps(Ops):
302334

@@ -742,6 +774,33 @@ def test_unknown_attribute(self):
742774
self.assertNotIn('foo',ts.__dict__.keys())
743775
self.assertRaises(AttributeError,lambda : ts.foo)
744776

777+
def test_getitem(self):
778+
idx1 = pd.timedelta_range('1 day', '31 day', freq='D', name='idx')
779+
780+
for idx in [idx1]:
781+
result = idx[0]
782+
self.assertEqual(result, pd.Timedelta('1 day'))
783+
784+
result = idx[0:5]
785+
expected = pd.timedelta_range('1 day', '5 day', freq='D', name='idx')
786+
self.assert_index_equal(result, expected)
787+
self.assertEqual(result.freq, expected.freq)
788+
789+
result = idx[0:10:2]
790+
expected = pd.timedelta_range('1 day', '9 day', freq='2D', name='idx')
791+
self.assert_index_equal(result, expected)
792+
self.assertEqual(result.freq, expected.freq)
793+
794+
result = idx[-20:-5:3]
795+
expected = pd.timedelta_range('12 day', '25 day', freq='3D', name='idx')
796+
self.assert_index_equal(result, expected)
797+
self.assertEqual(result.freq, expected.freq)
798+
799+
result = idx[4::-1]
800+
expected = TimedeltaIndex(['5 day', '4 day', '3 day', '2 day', '1 day'],
801+
freq='-1D', name='idx')
802+
self.assert_index_equal(result, expected)
803+
self.assertEqual(result.freq, expected.freq)
745804

746805
class TestPeriodIndexOps(Ops):
747806

0 commit comments

Comments
 (0)