diff --git a/doc/source/release.rst b/doc/source/release.rst index bb4bb71d8ceda..795c3cd642a80 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -532,6 +532,7 @@ Bug Fixes - Bug in ``isnull`` when applied to 0-dimensional object arrays (:issue:`7176`) - Bug in ``query``/``eval`` where global constants were not looked up correctly (:issue:`7178`) +- Bug in recognizing out-of-bounds positional list indexers with ``iloc`` and a multi-axis tuple indexer (:issue:`7189`) pandas 0.13.1 ------------- diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 1bd9f27dc926a..5359a045451c3 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1278,13 +1278,38 @@ def _has_valid_type(self, key, axis): "an indexable as a mask") return True - return (isinstance(key, slice) or - com.is_integer(key) or - _is_list_like(key)) + if isinstance(key, slice): + return True + elif com.is_integer(key): + return self._is_valid_integer(key, axis) + elif (_is_list_like(key)): + return self._is_valid_list_like(key, axis) + return False def _has_valid_setitem_indexer(self, indexer): self._has_valid_positional_setitem_indexer(indexer) + def _is_valid_integer(self, key, axis): + # return a boolean if we have a valid integer indexer + + ax = self.obj._get_axis(axis) + if key > len(ax): + raise IndexError("single positional indexer is out-of-bounds") + return True + + + def _is_valid_list_like(self, key, axis): + # return a boolean if we are a valid list-like (e.g. that we dont' have out-of-bounds values) + + # coerce the key to not exceed the maximum size of the index + arr = np.array(key) + ax = self.obj._get_axis(axis) + l = len(ax) + if len(arr) and (arr.max() >= l or arr.min() <= -l): + raise IndexError("positional indexers are out-of-bounds") + + return True + def _getitem_tuple(self, tup): self._has_valid_tuple(tup) @@ -1339,14 +1364,10 @@ def _getitem_axis(self, key, axis=0, validate_iterable=False): # a single integer or a list of integers else: - ax = self.obj._get_axis(axis) if _is_list_like(key): - # coerce the key to not exceed the maximum size of the index - arr = np.array(key) - l = len(ax) - if len(arr) and (arr.max() >= l or arr.min() <= -l): - raise IndexError("positional indexers are out-of-bounds") + # validate list bounds + self._is_valid_list_like(key, axis) # force an actual list key = list(key) @@ -1358,8 +1379,8 @@ def _getitem_axis(self, key, axis=0, validate_iterable=False): raise TypeError("Cannot index by location index with a " "non-integer key") - if key > len(ax): - raise IndexError("single positional indexer is out-of-bounds") + # validate the location + self._is_valid_integer(key, axis) return self._get_loc(key, axis=axis) diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py index ea2205c60d916..db27f9be867c6 100644 --- a/pandas/tests/test_indexing.py +++ b/pandas/tests/test_indexing.py @@ -1009,6 +1009,84 @@ def test_iloc_getitem_frame(self): # trying to use a label self.assertRaises(ValueError, df.iloc.__getitem__, tuple(['j','D'])) + def test_iloc_getitem_panel(self): + + # GH 7189 + p = Panel(np.arange(4*3*2).reshape(4,3,2), + items=['A','B','C','D'], + major_axis=['a','b','c'], + minor_axis=['one','two']) + + result = p.iloc[1] + expected = p.loc['B'] + assert_frame_equal(result, expected) + + result = p.iloc[1,1] + expected = p.loc['B','b'] + assert_series_equal(result, expected) + + result = p.iloc[1,1,1] + expected = p.loc['B','b','two'] + self.assertEqual(result,expected) + + # slice + result = p.iloc[1:3] + expected = p.loc[['B','C']] + assert_panel_equal(result, expected) + + result = p.iloc[:,0:2] + expected = p.loc[:,['a','b']] + assert_panel_equal(result, expected) + + # list of integers + result = p.iloc[[0,2]] + expected = p.loc[['A','C']] + assert_panel_equal(result, expected) + + # neg indicies + result = p.iloc[[-1,1],[-1,1]] + expected = p.loc[['D','B'],['c','b']] + assert_panel_equal(result, expected) + + # dups indicies + result = p.iloc[[-1,-1,1],[-1,1]] + expected = p.loc[['D','D','B'],['c','b']] + assert_panel_equal(result, expected) + + # combined + result = p.iloc[0,[True,True],[0,1]] + expected = p.loc['A',['a','b'],['one','two']] + assert_frame_equal(result, expected) + + # out-of-bounds exception + self.assertRaises(IndexError, p.iloc.__getitem__, tuple([10,5])) + def f(): + p.iloc[0,[True,True],[0,1,2]] + self.assertRaises(IndexError, f) + + # trying to use a label + self.assertRaises(ValueError, p.iloc.__getitem__, tuple(['j','D'])) + + # GH + p = Panel(np.random.rand(4,3,2), items=['A','B','C','D'], major_axis=['U','V','W'], minor_axis=['X','Y']) + expected = p['A'] + + result = p.iloc[0,:,:] + assert_frame_equal(result, expected) + + result = p.iloc[0,[True,True,True],:] + assert_frame_equal(result, expected) + + result = p.iloc[0,[True,True,True],[0,1]] + assert_frame_equal(result, expected) + + def f(): + p.iloc[0,[True,True,True],[0,1,2]] + self.assertRaises(IndexError, f) + + def f(): + p.iloc[0,[True,True,True],[2]] + self.assertRaises(IndexError, f) def test_iloc_getitem_doc_issue(self):