Skip to content

Commit c834758

Browse files
stephenwlinwesm
authored andcommitted
ENH: time slicing on datetime indicies
1 parent 68c0f34 commit c834758

File tree

4 files changed

+104
-26
lines changed

4 files changed

+104
-26
lines changed

pandas/core/index.py

+33-9
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,30 @@ def _wrap_joined_index(self, joined, other):
11171117
name = self.name if self.name == other.name else None
11181118
return Index(joined, name=name)
11191119

1120+
def slice_indexer(self, start=None, end=None, step=None):
1121+
"""
1122+
For an ordered Index, compute the slice indexer for input labels and
1123+
step
1124+
1125+
Parameters
1126+
----------
1127+
start : label, default None
1128+
If None, defaults to the beginning
1129+
end : label, default None
1130+
If None, defaults to the end
1131+
step : int, default None
1132+
1133+
Returns
1134+
-------
1135+
indexer : ndarray or slice
1136+
1137+
Notes
1138+
-----
1139+
This function assumes that the data is sorted, so use at your own peril
1140+
"""
1141+
start_slice, end_slice = self.slice_locs(start, end)
1142+
return slice(start_slice, end_slice, step)
1143+
11201144
def slice_locs(self, start=None, end=None):
11211145
"""
11221146
For an ordered Index, compute the slice locations for input labels
@@ -1125,27 +1149,27 @@ def slice_locs(self, start=None, end=None):
11251149
----------
11261150
start : label, default None
11271151
If None, defaults to the beginning
1128-
end : label
1152+
end : label, default None
11291153
If None, defaults to the end
11301154
11311155
Returns
11321156
-------
1133-
(begin, end) : (int, int)
1157+
(start, end) : (int, int)
11341158
11351159
Notes
11361160
-----
11371161
This function assumes that the data is sorted, so use at your own peril
11381162
"""
11391163
if start is None:
1140-
beg_slice = 0
1164+
start_slice = 0
11411165
else:
11421166
try:
1143-
beg_slice = self.get_loc(start)
1144-
if isinstance(beg_slice, slice):
1145-
beg_slice = beg_slice.start
1167+
start_slice = self.get_loc(start)
1168+
if isinstance(start_slice, slice):
1169+
start_slice = start_slice.start
11461170
except KeyError:
11471171
if self.is_monotonic:
1148-
beg_slice = self.searchsorted(start, side='left')
1172+
start_slice = self.searchsorted(start, side='left')
11491173
else:
11501174
raise
11511175

@@ -1164,7 +1188,7 @@ def slice_locs(self, start=None, end=None):
11641188
else:
11651189
raise
11661190

1167-
return beg_slice, end_slice
1191+
return start_slice, end_slice
11681192

11691193
def delete(self, loc):
11701194
"""
@@ -2106,7 +2130,7 @@ def slice_locs(self, start=None, end=None, strict=False):
21062130
21072131
Returns
21082132
-------
2109-
(begin, end) : (int, int)
2133+
(start, end) : (int, int)
21102134
21112135
Notes
21122136
-----

pandas/core/indexing.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -462,20 +462,19 @@ def _convert_to_indexer(self, obj, axis=0):
462462
raise
463463

464464
if null_slice or position_slice:
465-
slicer = obj
465+
indexer = obj
466466
else:
467467
try:
468-
i, j = labels.slice_locs(start, stop)
469-
slicer = slice(i, j, obj.step)
468+
indexer = labels.slice_indexer(start, stop, obj.step)
470469
except Exception:
471470
if _is_index_slice(obj):
472471
if labels.inferred_type == 'integer':
473472
raise
474-
slicer = obj
473+
indexer = obj
475474
else:
476475
raise
477476

478-
return slicer
477+
return indexer
479478

480479
elif _is_list_like(obj):
481480
if com._is_bool_indexer(obj):
@@ -535,8 +534,10 @@ def _tuplify(self, loc):
535534
def _get_slice_axis(self, slice_obj, axis=0):
536535
obj = self.obj
537536

538-
axis_name = obj._get_axis_name(axis)
539-
labels = getattr(obj, axis_name)
537+
if not _need_slice(slice_obj):
538+
return obj
539+
540+
labels = obj._get_axis(axis)
540541

541542
int_slice = _is_index_slice(slice_obj)
542543

@@ -569,23 +570,22 @@ def _get_slice_axis(self, slice_obj, axis=0):
569570
raise
570571

571572
if null_slice or position_slice:
572-
slicer = slice_obj
573+
indexer = slice_obj
573574
else:
574575
try:
575-
i, j = labels.slice_locs(start, stop)
576-
slicer = slice(i, j, slice_obj.step)
576+
indexer = labels.slice_indexer(start, stop, slice_obj.step)
577577
except Exception:
578578
if _is_index_slice(slice_obj):
579579
if labels.inferred_type == 'integer':
580580
raise
581-
slicer = slice_obj
581+
indexer = slice_obj
582582
else:
583583
raise
584584

585-
if not _need_slice(slice_obj):
586-
return obj
587-
588-
return self._slice(slicer, axis=axis)
585+
if isinstance(indexer, slice):
586+
return self._slice(indexer, axis=axis)
587+
else:
588+
return self.obj.take(indexer, axis=axis)
589589

590590
# 32-bit floating point machine epsilon
591591
_eps = np.finfo('f4').eps

pandas/tests/test_frame.py

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pylint: disable-msg=W0612,E1101
22
from copy import deepcopy
3-
from datetime import datetime, timedelta
3+
from datetime import datetime, timedelta, time
44
from StringIO import StringIO
55
import cPickle as pickle
66
import operator
@@ -4552,7 +4552,6 @@ def test_asfreq(self):
45524552
self.assert_(result is not zero_length)
45534553

45544554
def test_asfreq_datetimeindex(self):
4555-
from pandas import DatetimeIndex
45564555
df = DataFrame({'A': [1, 2, 3]},
45574556
index=[datetime(2011, 11, 01), datetime(2011, 11, 2),
45584557
datetime(2011, 11, 3)])
@@ -4562,6 +4561,44 @@ def test_asfreq_datetimeindex(self):
45624561
ts = df['A'].asfreq('B')
45634562
self.assert_(isinstance(ts.index, DatetimeIndex))
45644563

4564+
def test_attime_betweentime_datetimeindex(self):
4565+
index = pan.date_range("2012-01-01", "2012-01-05", freq='30min')
4566+
df = DataFrame(randn(len(index), 5), index=index)
4567+
4568+
result = df.at_time(time(12, 0, 0))
4569+
expected = df.ix[time(12, 0, 0)]
4570+
assert_frame_equal(result, expected)
4571+
self.assert_(len(result) == 4)
4572+
4573+
result = df.between_time(time(13, 0, 0), time(14, 0, 0))
4574+
expected = df.ix[time(13, 0, 0):time(14, 0, 0)]
4575+
assert_frame_equal(result, expected)
4576+
self.assert_(len(result) == 12)
4577+
4578+
result = df.copy()
4579+
result.ix[time(12, 0, 0)] = 0
4580+
result = result.ix[time(12, 0, 0)]
4581+
expected = df.ix[time(12, 0, 0)].copy()
4582+
expected.ix[:] = 0
4583+
assert_frame_equal(result, expected)
4584+
4585+
result = df.copy()
4586+
result.ix[time(12, 0, 0)] = 0
4587+
result.ix[time(12, 0, 0)] = df # also checks reindexing
4588+
assert_frame_equal(result, df)
4589+
4590+
result = df.copy()
4591+
result.ix[time(13, 0, 0):time(14, 0, 0)] = 0
4592+
result = result.ix[time(13, 0, 0):time(14, 0, 0)]
4593+
expected = df.ix[time(13, 0, 0):time(14, 0, 0)].copy()
4594+
expected.ix[:] = 0
4595+
assert_frame_equal(result, expected)
4596+
4597+
result = df.copy()
4598+
result.ix[time(13, 0, 0):time(14, 0, 0)] = 0
4599+
result.ix[time(13, 0, 0):time(14, 0, 0)] = df # also checks reindexing
4600+
assert_frame_equal(result, df)
4601+
45654602
def test_as_matrix(self):
45664603
frame = self.frame
45674604
mat = frame.as_matrix()

pandas/tseries/index.py

+17
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,20 @@ def _get_string_slice(self, key):
11521152
loc = self._partial_date_slice(reso, parsed)
11531153
return loc
11541154

1155+
def slice_indexer(self, start=None, end=None, step=None):
1156+
"""
1157+
Index.slice_indexer, customized to handle time slicing
1158+
"""
1159+
if isinstance(start, time) and isinstance(end, time):
1160+
if step is not None and step != 1:
1161+
raise ValueError('Must have step size of 1 with time slices')
1162+
return self.indexer_between_time(start, end)
1163+
1164+
if isinstance(start, time) or isinstance(end, time):
1165+
raise KeyError('Cannot mix time and non-time slice keys')
1166+
1167+
return Index.slice_indexer(self, start, end, step)
1168+
11551169
def slice_locs(self, start=None, end=None):
11561170
"""
11571171
Index.slice_locs, customized to handle partial ISO-8601 string slicing
@@ -1172,6 +1186,9 @@ def slice_locs(self, start=None, end=None):
11721186
except KeyError:
11731187
pass
11741188

1189+
if isinstance(start, time) or isinstance(end, time):
1190+
raise KeyError('Cannot use slice_locs with time slice keys')
1191+
11751192
return Index.slice_locs(self, start, end)
11761193

11771194
def __getitem__(self, key):

0 commit comments

Comments
 (0)