Skip to content

Commit 9d01fe1

Browse files
committed
Merge pull request #7189 from jreback/panel_iloc
BUG: bug in recognizing out-of-bounds on iloc indexers on tuple indexers (GH )
2 parents be45e0d + d345c9c commit 9d01fe1

File tree

3 files changed

+111
-11
lines changed

3 files changed

+111
-11
lines changed

doc/source/release.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ Bug Fixes
532532
- Bug in ``isnull`` when applied to 0-dimensional object arrays (:issue:`7176`)
533533
- Bug in ``query``/``eval`` where global constants were not looked up correctly
534534
(:issue:`7178`)
535+
- Bug in recognizing out-of-bounds positional list indexers with ``iloc`` and a multi-axis tuple indexer (:issue:`7189`)
535536

536537
pandas 0.13.1
537538
-------------

pandas/core/indexing.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,13 +1278,38 @@ def _has_valid_type(self, key, axis):
12781278
"an indexable as a mask")
12791279
return True
12801280

1281-
return (isinstance(key, slice) or
1282-
com.is_integer(key) or
1283-
_is_list_like(key))
1281+
if isinstance(key, slice):
1282+
return True
1283+
elif com.is_integer(key):
1284+
return self._is_valid_integer(key, axis)
1285+
elif (_is_list_like(key)):
1286+
return self._is_valid_list_like(key, axis)
1287+
return False
12841288

12851289
def _has_valid_setitem_indexer(self, indexer):
12861290
self._has_valid_positional_setitem_indexer(indexer)
12871291

1292+
def _is_valid_integer(self, key, axis):
1293+
# return a boolean if we have a valid integer indexer
1294+
1295+
ax = self.obj._get_axis(axis)
1296+
if key > len(ax):
1297+
raise IndexError("single positional indexer is out-of-bounds")
1298+
return True
1299+
1300+
1301+
def _is_valid_list_like(self, key, axis):
1302+
# return a boolean if we are a valid list-like (e.g. that we dont' have out-of-bounds values)
1303+
1304+
# coerce the key to not exceed the maximum size of the index
1305+
arr = np.array(key)
1306+
ax = self.obj._get_axis(axis)
1307+
l = len(ax)
1308+
if len(arr) and (arr.max() >= l or arr.min() <= -l):
1309+
raise IndexError("positional indexers are out-of-bounds")
1310+
1311+
return True
1312+
12881313
def _getitem_tuple(self, tup):
12891314

12901315
self._has_valid_tuple(tup)
@@ -1339,14 +1364,10 @@ def _getitem_axis(self, key, axis=0, validate_iterable=False):
13391364
# a single integer or a list of integers
13401365
else:
13411366

1342-
ax = self.obj._get_axis(axis)
13431367
if _is_list_like(key):
13441368

1345-
# coerce the key to not exceed the maximum size of the index
1346-
arr = np.array(key)
1347-
l = len(ax)
1348-
if len(arr) and (arr.max() >= l or arr.min() <= -l):
1349-
raise IndexError("positional indexers are out-of-bounds")
1369+
# validate list bounds
1370+
self._is_valid_list_like(key, axis)
13501371

13511372
# force an actual list
13521373
key = list(key)
@@ -1358,8 +1379,8 @@ def _getitem_axis(self, key, axis=0, validate_iterable=False):
13581379
raise TypeError("Cannot index by location index with a "
13591380
"non-integer key")
13601381

1361-
if key > len(ax):
1362-
raise IndexError("single positional indexer is out-of-bounds")
1382+
# validate the location
1383+
self._is_valid_integer(key, axis)
13631384

13641385
return self._get_loc(key, axis=axis)
13651386

pandas/tests/test_indexing.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,84 @@ def test_iloc_getitem_frame(self):
10091009
# trying to use a label
10101010
self.assertRaises(ValueError, df.iloc.__getitem__, tuple(['j','D']))
10111011

1012+
def test_iloc_getitem_panel(self):
1013+
1014+
# GH 7189
1015+
p = Panel(np.arange(4*3*2).reshape(4,3,2),
1016+
items=['A','B','C','D'],
1017+
major_axis=['a','b','c'],
1018+
minor_axis=['one','two'])
1019+
1020+
result = p.iloc[1]
1021+
expected = p.loc['B']
1022+
assert_frame_equal(result, expected)
1023+
1024+
result = p.iloc[1,1]
1025+
expected = p.loc['B','b']
1026+
assert_series_equal(result, expected)
1027+
1028+
result = p.iloc[1,1,1]
1029+
expected = p.loc['B','b','two']
1030+
self.assertEqual(result,expected)
1031+
1032+
# slice
1033+
result = p.iloc[1:3]
1034+
expected = p.loc[['B','C']]
1035+
assert_panel_equal(result, expected)
1036+
1037+
result = p.iloc[:,0:2]
1038+
expected = p.loc[:,['a','b']]
1039+
assert_panel_equal(result, expected)
1040+
1041+
# list of integers
1042+
result = p.iloc[[0,2]]
1043+
expected = p.loc[['A','C']]
1044+
assert_panel_equal(result, expected)
1045+
1046+
# neg indicies
1047+
result = p.iloc[[-1,1],[-1,1]]
1048+
expected = p.loc[['D','B'],['c','b']]
1049+
assert_panel_equal(result, expected)
1050+
1051+
# dups indicies
1052+
result = p.iloc[[-1,-1,1],[-1,1]]
1053+
expected = p.loc[['D','D','B'],['c','b']]
1054+
assert_panel_equal(result, expected)
1055+
1056+
# combined
1057+
result = p.iloc[0,[True,True],[0,1]]
1058+
expected = p.loc['A',['a','b'],['one','two']]
1059+
assert_frame_equal(result, expected)
1060+
1061+
# out-of-bounds exception
1062+
self.assertRaises(IndexError, p.iloc.__getitem__, tuple([10,5]))
1063+
def f():
1064+
p.iloc[0,[True,True],[0,1,2]]
1065+
self.assertRaises(IndexError, f)
1066+
1067+
# trying to use a label
1068+
self.assertRaises(ValueError, p.iloc.__getitem__, tuple(['j','D']))
1069+
1070+
# GH
1071+
p = Panel(np.random.rand(4,3,2), items=['A','B','C','D'], major_axis=['U','V','W'], minor_axis=['X','Y'])
1072+
expected = p['A']
1073+
1074+
result = p.iloc[0,:,:]
1075+
assert_frame_equal(result, expected)
1076+
1077+
result = p.iloc[0,[True,True,True],:]
1078+
assert_frame_equal(result, expected)
1079+
1080+
result = p.iloc[0,[True,True,True],[0,1]]
1081+
assert_frame_equal(result, expected)
1082+
1083+
def f():
1084+
p.iloc[0,[True,True,True],[0,1,2]]
1085+
self.assertRaises(IndexError, f)
1086+
1087+
def f():
1088+
p.iloc[0,[True,True,True],[2]]
1089+
self.assertRaises(IndexError, f)
10121090

10131091
def test_iloc_getitem_doc_issue(self):
10141092

0 commit comments

Comments
 (0)