diff --git a/RELEASE.rst b/RELEASE.rst index 2eb7980458f8e..f4c13e5cbca98 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -38,7 +38,7 @@ pandas 0.11.0 - Add ``squeeze`` function to reduce dimensionality of 1-len objects - Support slicing with time objects (GH2681_) - Added ``.iloc`` attribute, to support strict integer based indexing, analagous to ``.ix`` (GH2922_) - - Added ``.loc`` attribute, to support strict label based indexing, analagous to ``.ix`` + - Added ``.loc`` attribute, to support strict label based indexing, analagous to ``.ix`` (GH3053_) - Added ``.iat`` attribute, to support fast scalar access via integers (replaces ``iget_value/iset_value``) - Added ``.at`` attribute, to support fast scalar access via labels (replaces ``get_value/set_value``) - Moved functionaility from ``irow,icol,iget_value/iset_value`` to ``.iloc`` indexer @@ -183,6 +183,7 @@ pandas 0.11.0 .. _GH3012: https://github.com/pydata/pandas/issues/3012 .. _GH3029: https://github.com/pydata/pandas/issues/3029 .. _GH3041: https://github.com/pydata/pandas/issues/3041 +.. _GH3053: https://github.com/pydata/pandas/issues/3053 pandas 0.10.1 diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 17423075b6bfb..cab9e967519de 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -647,6 +647,19 @@ def _getbool_axis(self, key, axis=0): return self.obj.take(inds, axis=axis) except (Exception), detail: raise self._exception(detail) + def _get_slice_axis(self, slice_obj, axis=0): + """ this is pretty simple as we just have to deal with labels """ + obj = self.obj + if not _need_slice(slice_obj): + return obj + + labels = obj._get_axis(axis) + indexer = labels.slice_indexer(slice_obj.start, slice_obj.stop, slice_obj.step) + + if isinstance(indexer, slice): + return self._slice(indexer, axis=axis) + else: + return self.obj.take(indexer, axis=axis) class _LocIndexer(_LocationIndexer): """ purely label based location based indexing """ @@ -667,11 +680,8 @@ def _has_valid_type(self, key, axis): if key.start not in ax: raise KeyError("start bound [%s] is not the [%s]" % (key.start,self.obj._get_axis_name(axis))) if key.stop is not None: - stop = key.stop - if com.is_integer(stop): - stop -= 1 - if stop not in ax: - raise KeyError("stop bound [%s] is not in the [%s]" % (stop,self.obj._get_axis_name(axis))) + if key.stop not in ax: + raise KeyError("stop bound [%s] is not in the [%s]" % (key.stop,self.obj._get_axis_name(axis))) elif com._is_bool_indexer(key): return True @@ -700,9 +710,6 @@ def _getitem_axis(self, key, axis=0): labels = self.obj._get_axis(axis) if isinstance(key, slice): - ltype = labels.inferred_type - if ltype == 'mixed-integer-float' or ltype == 'mixed-integer': - raise ValueError('cannot slice with a non-single type label array') return self._get_slice_axis(key, axis=axis) elif com._is_bool_indexer(key): return self._getbool_axis(key, axis=axis) diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py index e48d8dbdcb498..f1ac1a288d45a 100644 --- a/pandas/tests/test_indexing.py +++ b/pandas/tests/test_indexing.py @@ -450,12 +450,38 @@ def test_loc_getitem_bool(self): def test_loc_getitem_int_slice(self): # int slices in int - self.check_result('int slice1', 'loc', slice(1,3), 'ix', { 0 : [2,4], 1: [3,6], 2: [4,8] }, typs = ['ints'], fails=KeyError) + self.check_result('int slice1', 'loc', slice(2,4), 'ix', { 0 : [2,4], 1: [3,6], 2: [4,8] }, typs = ['ints'], fails=KeyError) # ok - self.check_result('int slice2', 'loc', slice(2,5), 'ix', [2,4], typs = ['ints'], axes = 0) - self.check_result('int slice2', 'loc', slice(3,7), 'ix', [3,6], typs = ['ints'], axes = 1) - self.check_result('int slice2', 'loc', slice(4,9), 'ix', [4,8], typs = ['ints'], axes = 2) + self.check_result('int slice2', 'loc', slice(2,4), 'ix', [2,4], typs = ['ints'], axes = 0) + self.check_result('int slice2', 'loc', slice(3,6), 'ix', [3,6], typs = ['ints'], axes = 1) + self.check_result('int slice2', 'loc', slice(4,8), 'ix', [4,8], typs = ['ints'], axes = 2) + + # GH 3053 + # loc should treat integer slices like label slices + from itertools import product + + index = MultiIndex.from_tuples([t for t in product([6,7,8], ['a', 'b'])]) + df = DataFrame(np.random.randn(6, 6), index, index) + result = df.loc[6:8,:] + expected = df.ix[6:8,:] + assert_frame_equal(result,expected) + + index = MultiIndex.from_tuples([t for t in product([10, 20, 30], ['a', 'b'])]) + df = DataFrame(np.random.randn(6, 6), index, index) + result = df.loc[20:30,:] + expected = df.ix[20:30,:] + assert_frame_equal(result,expected) + + # doc examples + result = df.loc[10,:] + expected = df.ix[10,:] + assert_frame_equal(result,expected) + + result = df.loc[:,10] + #expected = df.ix[:,10] (this fails) + expected = df[10] + assert_frame_equal(result,expected) def test_loc_getitem_label_slice(self): @@ -475,8 +501,7 @@ def test_loc_getitem_label_slice(self): self.check_result('mixed slice', 'loc', slice(2,8), 'ix', slice(2,8), typs = ['mixed'], axes=1, fails=KeyError) self.check_result('mixed slice', 'loc', slice(2,8), 'ix', slice(2,8), typs = ['mixed'], axes=2, fails=KeyError) - # you would think this would work, but we don't have an ordering, so fail - self.check_result('mixed slice', 'loc', slice(2,5,2), 'ix', slice(2,4,2), typs = ['mixed'], axes=0, fails=ValueError) + self.check_result('mixed slice', 'loc', slice(2,4,2), 'ix', slice(2,4,2), typs = ['mixed'], axes=0) def test_loc_general(self):