Repository: pandas-dev/pandas

Issue:
BUG: Series.iloc[[-1]] error when Series is of length 1
When a `Series` is of length one, I would expect `iloc[[-1]]` to produce the same result as `iloc[[0]]`.  However `iloc[[-1]]` produces an error, even though `iloc[[0]]` and `iloc[-1]` work.

```
In [178]: import pandas as pd

In [179]: pd.Series(['a', 'b'], index=['A', 'B']).iloc[[-1]]
Out[179]:
B    b
dtype: object

In [180]: pd.Series(['a'], index=['A']).iloc[[-1]]  # BUG: This should return the same as [181].
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-180-47228385ee31> in <module>()
----> 1 pd.Series(['a'], index=['A']).iloc[[-1]]

C:\Python34\lib\site-packages\pandas\core\indexing.py in __getitem__(self, key)
   1187             return self._getitem_tuple(key)
   1188         else:
-> 1189             return self._getitem_axis(key, axis=0)
   1190
   1191     def _getitem_axis(self, key, axis=0):

C:\Python34\lib\site-packages\pandas\core\indexing.py in _getitem_axis(self, key, axis)
   1463
   1464                 # validate list bounds
-> 1465                 self._is_valid_list_like(key, axis)
   1466
   1467                 # force an actual list

C:\Python34\lib\site-packages\pandas\core\indexing.py in _is_valid_list_like(self, key, axis)
   1402         l = len(ax)
   1403         if len(arr) and (arr.max() >= l or arr.min() <= -l):
-> 1404             raise IndexError("positional indexers are out-of-bounds")
   1405
   1406         return True

IndexError: positional indexers are out-of-bounds

In [181]: pd.Series(['a'], index=['A']).iloc[[0]]
Out[181]:
A    a
dtype: object

In [182]: pd.Series(['a'], index=['A']).iloc[-1]
Out[182]: 'a'

In [183]: pd.__version__
Out[183]: '0.16.2'
```



Discussion:
I think the test should be `arr.min() < -l` (and not `<=`). pull-request to fix?


Resolving patch:
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -1388,7 +1388,8 @@ 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):
+        l = len(ax)
+        if key >= l or key < -l:
             raise IndexError("single positional indexer is out-of-bounds")
         return True
 
@@ -1400,7 +1401,7 @@ def _is_valid_list_like(self, key, axis):
         arr = np.array(key)
         ax = self.obj._get_axis(axis)
         l = len(ax)
-        if len(arr) and (arr.max() >= l or arr.min() <= -l):
+        if len(arr) and (arr.max() >= l or arr.min() < -l):
             raise IndexError("positional indexers are out-of-bounds")
 
         return True


Test patch:
diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py
--- a/pandas/tests/test_indexing.py
+++ b/pandas/tests/test_indexing.py
@@ -411,6 +411,12 @@ def test_iloc_exceeds_bounds(self):
             df.iloc[30]
         self.assertRaises(IndexError, lambda : df.iloc[-30])
 
+        # GH10779 
+        # single positive/negative indexer exceeding Series bounds should raise an IndexError
+        with tm.assertRaisesRegexp(IndexError, 'single positional indexer is out-of-bounds'):
+            s.iloc[30]
+        self.assertRaises(IndexError, lambda : s.iloc[-30])
+
         # slices are ok
         result = df.iloc[:,4:10]  # 0 < start < len < stop
         expected = df.iloc[:,4:]
@@ -471,7 +477,6 @@ def check(result,expected):
         self.assertRaises(IndexError, lambda : dfl.iloc[[4,5,6]])
         self.assertRaises(IndexError, lambda : dfl.iloc[:,4])
 
-
     def test_iloc_getitem_int(self):
 
         # integer
@@ -497,6 +502,33 @@ def test_iloc_getitem_list_int(self):
         self.check_result('array int', 'iloc', np.array([2]), 'ix', { 0 : [4], 1 : [6], 2: [8] }, typs = ['ints'])
         self.check_result('array int', 'iloc', np.array([0,1,2]), 'indexer', [0,1,2], typs = ['labels','mixed','ts','floats','empty'], fails = IndexError)
 
+    def test_iloc_getitem_neg_int_can_reach_first_index(self):
+        # GH10547 and GH10779
+        # negative integers should be able to reach index 0
+        df = DataFrame({'A': [2, 3, 5], 'B': [7, 11, 13]})
+        s = df['A']
+
+        expected = df.iloc[0]
+        result = df.iloc[-3]
+        assert_series_equal(result, expected)
+
+        expected = df.iloc[[0]]
+        result = df.iloc[[-3]]
+        assert_frame_equal(result, expected)
+
+        expected = s.iloc[0]
+        result = s.iloc[-3]
+        self.assertEqual(result, expected)
+
+        expected = s.iloc[[0]]
+        result = s.iloc[[-3]]
+        assert_series_equal(result, expected)
+
+        # check the length 1 Series case highlighted in GH10547
+        expected = pd.Series(['a'], index=['A'])
+        result = expected.iloc[[-1]]
+        assert_series_equal(result, expected)
+
     def test_iloc_getitem_dups(self):
 
         # no dups in panel (bug?)