Skip to content

Commit bcc7b0b

Browse files
committed
Merge pull request pandas-dev#10808 from ajcr/iloc-negative-index
BUG: fix bounds for negative ints when using iloc (GH 10779)
2 parents 63c587d + c0ff3e7 commit bcc7b0b

File tree

3 files changed

+38
-3
lines changed

3 files changed

+38
-3
lines changed

doc/source/whatsnew/v0.17.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -658,3 +658,5 @@ Bug Fixes
658658
- Bug in ``DatetimeIndex.take`` and ``TimedeltaIndex.take`` may not raise ``IndexError`` against invalid index (:issue:`10295`)
659659
- Bug in ``Series([np.nan]).astype('M8[ms]')``, which now returns ``Series([pd.NaT])`` (:issue:`10747`)
660660
- Bug in ``PeriodIndex.order`` reset freq (:issue:`10295`)
661+
- Bug in ``iloc`` allowing memory outside bounds of a Series to be accessed with negative integers (:issue:`10779`)
662+
- Bug preventing access to the first index when using ``iloc`` with a list containing the appropriate negative integer (:issue:`10547`, :issue:`10779`)

pandas/core/indexing.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1388,7 +1388,8 @@ def _is_valid_integer(self, key, axis):
13881388
# return a boolean if we have a valid integer indexer
13891389

13901390
ax = self.obj._get_axis(axis)
1391-
if key > len(ax):
1391+
l = len(ax)
1392+
if key >= l or key < -l:
13921393
raise IndexError("single positional indexer is out-of-bounds")
13931394
return True
13941395

@@ -1400,7 +1401,7 @@ def _is_valid_list_like(self, key, axis):
14001401
arr = np.array(key)
14011402
ax = self.obj._get_axis(axis)
14021403
l = len(ax)
1403-
if len(arr) and (arr.max() >= l or arr.min() <= -l):
1404+
if len(arr) and (arr.max() >= l or arr.min() < -l):
14041405
raise IndexError("positional indexers are out-of-bounds")
14051406

14061407
return True

pandas/tests/test_indexing.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,12 @@ def test_iloc_exceeds_bounds(self):
411411
df.iloc[30]
412412
self.assertRaises(IndexError, lambda : df.iloc[-30])
413413

414+
# GH10779
415+
# single positive/negative indexer exceeding Series bounds should raise an IndexError
416+
with tm.assertRaisesRegexp(IndexError, 'single positional indexer is out-of-bounds'):
417+
s.iloc[30]
418+
self.assertRaises(IndexError, lambda : s.iloc[-30])
419+
414420
# slices are ok
415421
result = df.iloc[:,4:10] # 0 < start < len < stop
416422
expected = df.iloc[:,4:]
@@ -471,7 +477,6 @@ def check(result,expected):
471477
self.assertRaises(IndexError, lambda : dfl.iloc[[4,5,6]])
472478
self.assertRaises(IndexError, lambda : dfl.iloc[:,4])
473479

474-
475480
def test_iloc_getitem_int(self):
476481

477482
# integer
@@ -497,6 +502,33 @@ def test_iloc_getitem_list_int(self):
497502
self.check_result('array int', 'iloc', np.array([2]), 'ix', { 0 : [4], 1 : [6], 2: [8] }, typs = ['ints'])
498503
self.check_result('array int', 'iloc', np.array([0,1,2]), 'indexer', [0,1,2], typs = ['labels','mixed','ts','floats','empty'], fails = IndexError)
499504

505+
def test_iloc_getitem_neg_int_can_reach_first_index(self):
506+
# GH10547 and GH10779
507+
# negative integers should be able to reach index 0
508+
df = DataFrame({'A': [2, 3, 5], 'B': [7, 11, 13]})
509+
s = df['A']
510+
511+
expected = df.iloc[0]
512+
result = df.iloc[-3]
513+
assert_series_equal(result, expected)
514+
515+
expected = df.iloc[[0]]
516+
result = df.iloc[[-3]]
517+
assert_frame_equal(result, expected)
518+
519+
expected = s.iloc[0]
520+
result = s.iloc[-3]
521+
self.assertEqual(result, expected)
522+
523+
expected = s.iloc[[0]]
524+
result = s.iloc[[-3]]
525+
assert_series_equal(result, expected)
526+
527+
# check the length 1 Series case highlighted in GH10547
528+
expected = pd.Series(['a'], index=['A'])
529+
result = expected.iloc[[-1]]
530+
assert_series_equal(result, expected)
531+
500532
def test_iloc_getitem_dups(self):
501533

502534
# no dups in panel (bug?)

0 commit comments

Comments
 (0)