From f865b721c17596fc918c29992e495868ac65578b Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Sun, 17 Mar 2019 20:55:44 -0400 Subject: [PATCH 01/10] better error message for iloc called with non-integer keys --- pandas/core/indexing.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 623a48acdd48b..cf3c9251d4add 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2079,6 +2079,10 @@ def _validate_key(self, key, axis): arr = np.array(key) len_axis = len(self.obj._get_axis(axis)) + if not np.issubdtype(arr.dtype, np.int): + raise IndexError(".iloc requires integer indexers, found " + "type {dtype}".format(dtype=arr.dtype)) + if len(arr) and (arr.max() >= len_axis or arr.min() < -len_axis): raise IndexError("positional indexers are out-of-bounds") else: From 1861a1a2af0b2f7b4d9e7897bc9556fbc91ce4cb Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Sun, 17 Mar 2019 21:04:37 -0400 Subject: [PATCH 02/10] updated whatsnew --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 1d2466adf9265..52cd30ba97381 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -204,7 +204,7 @@ Interval Indexing ^^^^^^^^ -- +- Added an exception message when calling :meth:`DataFrame.iloc` with a list of non-integer objects. - - From 4cb46674a7092171481c75889563a014b410260c Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Sun, 17 Mar 2019 21:38:11 -0400 Subject: [PATCH 03/10] change from np.int -> np.numeric --- pandas/core/indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index cf3c9251d4add..4a6cd18b98917 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2079,7 +2079,7 @@ def _validate_key(self, key, axis): arr = np.array(key) len_axis = len(self.obj._get_axis(axis)) - if not np.issubdtype(arr.dtype, np.int): + if not np.issubdtype(arr.dtype, np.number): raise IndexError(".iloc requires integer indexers, found " "type {dtype}".format(dtype=arr.dtype)) From 0b00f3e91e91fa6e3c3686178a0de0c8f2781338 Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Sun, 17 Mar 2019 22:41:30 -0400 Subject: [PATCH 04/10] added tests for new exception --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/indexing.py | 4 ++-- pandas/tests/indexing/test_iloc.py | 34 ++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 52cd30ba97381..3944cc725f0ed 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -204,7 +204,7 @@ Interval Indexing ^^^^^^^^ -- Added an exception message when calling :meth:`DataFrame.iloc` with a list of non-integer objects. +- Added an exception message when calling :meth:`DataFrame.iloc` with a list of non-numeric objects. - - diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 4a6cd18b98917..d6f6913d30c4b 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2080,8 +2080,8 @@ def _validate_key(self, key, axis): len_axis = len(self.obj._get_axis(axis)) if not np.issubdtype(arr.dtype, np.number): - raise IndexError(".iloc requires integer indexers, found " - "type {dtype}".format(dtype=arr.dtype)) + raise IndexError(".iloc requires integer indexers, got " + "{arr}".format(arr=arr)) if len(arr) and (arr.max() >= len_axis or arr.min() < -len_axis): raise IndexError("positional indexers are out-of-bounds") diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index 7147757953b01..2505eacee1ca2 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -118,6 +118,40 @@ def check(result, expected): with pytest.raises(IndexError, match=msg): dfl.iloc[:, 4] + def test_iloc_non_integer(self): + + # iloc should throw IndexError if non-numeric list is passed + df = DataFrame(np.random.random_sample((20, 5)), columns=list('ABCDE')) + + msg = '.iloc requires integer indexers, got' + with pytest.raises(IndexError, match=msg): + df.iloc[:, ['A', 'D']] + with pytest.raises(IndexError, match=msg): + df.iloc[['1', '2'], :] + with pytest.raises(IndexError, match=msg): + df.iloc[[pd.datetime(2019, 1, 1)], :] + + # Numeric or booleans should not raise errors + result = df.iloc[:, [0, 1]] + expected = df.loc[:, ['A', 'B']] + tm.assert_frame_equal(result, expected) + + result = df.iloc[[0, 1], [0, 1, 3]] + expected = df.loc[[0, 1], ['A', 'B', 'D']] + tm.assert_frame_equal(result, expected) + + result = df.iloc[:, [True, False, False, True]] + expected = df.loc[:, ['A', 'D']] + tm.assert_frame_equal(result, expected) + + result = df.iloc[:, [0, 0.5]] + expected = df.loc[:, ['A', 'A']] + tm.assert_frame_equal(result, expected) + + result = df.iloc[[True, False, True], :] + expected = df.loc[[0, 2], :] + tm.assert_frame_equal(result, expected) + def test_iloc_getitem_int(self): # integer From 6d94f4ad5c9a2d9602f4ed4c2fb99946a7374259 Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Mon, 18 Mar 2019 09:40:58 -0400 Subject: [PATCH 05/10] revise parameterized tests --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/tests/indexing/test_iloc.py | 43 +++++++++--------------------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 3944cc725f0ed..c77f282655e43 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -204,7 +204,7 @@ Interval Indexing ^^^^^^^^ -- Added an exception message when calling :meth:`DataFrame.iloc` with a list of non-numeric objects. +- Added an exception message when calling :meth:`DataFrame.iloc` with a list of non-numeric objects (:issue:`25753`). - - diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index 2505eacee1ca2..5cb4e507ea15f 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -118,39 +118,20 @@ def check(result, expected): with pytest.raises(IndexError, match=msg): dfl.iloc[:, 4] - def test_iloc_non_integer(self): - - # iloc should throw IndexError if non-numeric list is passed - df = DataFrame(np.random.random_sample((20, 5)), columns=list('ABCDE')) - + @pytest.mark.parametrize("index,columns", [(np.arange(20), list('ABCDE'))]) + @pytest.mark.parametrize("index_vals,column_vals", [ + ([slice(None), ['A', 'D']]), + (['1', '2'], slice(None)), + ([pd.datetime(2019, 1, 1)], slice(None))]) + def test_iloc_non_integer_raises(self, index, columns, + index_vals, column_vals): + # GH 25753 + df = DataFrame(np.random.randn(len(index), len(columns)), + index=index, + columns=columns) msg = '.iloc requires integer indexers, got' with pytest.raises(IndexError, match=msg): - df.iloc[:, ['A', 'D']] - with pytest.raises(IndexError, match=msg): - df.iloc[['1', '2'], :] - with pytest.raises(IndexError, match=msg): - df.iloc[[pd.datetime(2019, 1, 1)], :] - - # Numeric or booleans should not raise errors - result = df.iloc[:, [0, 1]] - expected = df.loc[:, ['A', 'B']] - tm.assert_frame_equal(result, expected) - - result = df.iloc[[0, 1], [0, 1, 3]] - expected = df.loc[[0, 1], ['A', 'B', 'D']] - tm.assert_frame_equal(result, expected) - - result = df.iloc[:, [True, False, False, True]] - expected = df.loc[:, ['A', 'D']] - tm.assert_frame_equal(result, expected) - - result = df.iloc[:, [0, 0.5]] - expected = df.loc[:, ['A', 'A']] - tm.assert_frame_equal(result, expected) - - result = df.iloc[[True, False, True], :] - expected = df.loc[[0, 2], :] - tm.assert_frame_equal(result, expected) + df.iloc[index_vals, column_vals] def test_iloc_getitem_int(self): From 1f0302fc7d815643796a1e7b8281bf40b0afbfd6 Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Tue, 19 Mar 2019 07:38:22 -0400 Subject: [PATCH 06/10] modified whatsnew --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index c77f282655e43..49a07ce585a0d 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -204,7 +204,7 @@ Interval Indexing ^^^^^^^^ -- Added an exception message when calling :meth:`DataFrame.iloc` with a list of non-numeric objects (:issue:`25753`). +- Improved exception message when calling :meth:`DataFrame.iloc` with a list of non-numeric objects (:issue:`25753`). - - From 3659f048df2cd5bf2969160ef5dc49bf343eb7b9 Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Tue, 19 Mar 2019 12:39:34 -0400 Subject: [PATCH 07/10] clarify commenting around key checks --- pandas/core/indexing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index d6f6913d30c4b..d80f782d50c66 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2075,14 +2075,15 @@ def _validate_key(self, key, axis): # so don't treat a tuple as a valid indexer raise IndexingError('Too many indexers') elif is_list_like_indexer(key): - # check that the key does not exceed the maximum size of the index arr = np.array(key) len_axis = len(self.obj._get_axis(axis)) + # check that the key has a numeric dtype if not np.issubdtype(arr.dtype, np.number): - raise IndexError(".iloc requires integer indexers, got " + raise IndexError(".iloc requires numeric indexers, got " "{arr}".format(arr=arr)) + # check that the key does not exceed the maximum size of the index if len(arr) and (arr.max() >= len_axis or arr.min() < -len_axis): raise IndexError("positional indexers are out-of-bounds") else: From 317f9bfb02d6f242a3afe07e306db95e7fe386e7 Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Tue, 19 Mar 2019 13:52:12 -0400 Subject: [PATCH 08/10] update tests to match new exception --- pandas/tests/indexing/test_iloc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index 5cb4e507ea15f..27f62af8394ef 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -129,7 +129,7 @@ def test_iloc_non_integer_raises(self, index, columns, df = DataFrame(np.random.randn(len(index), len(columns)), index=index, columns=columns) - msg = '.iloc requires integer indexers, got' + msg = '.iloc requires numeric indexers, got' with pytest.raises(IndexError, match=msg): df.iloc[index_vals, column_vals] From f816fd9ed901fc5db17411fbf9765aac69470118 Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Tue, 19 Mar 2019 15:30:12 -0400 Subject: [PATCH 09/10] change to is_numeric_dtype --- pandas/core/indexing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index d80f782d50c66..81329a2702cb3 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -13,7 +13,7 @@ from pandas.core.dtypes.common import ( ensure_platform_int, is_float, is_integer, is_integer_dtype, is_iterator, - is_list_like, is_scalar, is_sequence, is_sparse) + is_list_like, is_scalar, is_sequence, is_sparse, is_numeric_dtype) from pandas.core.dtypes.generic import ABCDataFrame, ABCPanel, ABCSeries from pandas.core.dtypes.missing import _infer_fill_value, isna @@ -2079,7 +2079,7 @@ def _validate_key(self, key, axis): len_axis = len(self.obj._get_axis(axis)) # check that the key has a numeric dtype - if not np.issubdtype(arr.dtype, np.number): + if not is_numeric_dtype(arr.dtype): raise IndexError(".iloc requires numeric indexers, got " "{arr}".format(arr=arr)) From a8caad006b64fcb17d1e8b8f7029443b79eb4ebb Mon Sep 17 00:00:00 2001 From: Kyle Kosic Date: Tue, 19 Mar 2019 15:58:19 -0400 Subject: [PATCH 10/10] alphabetize imports --- pandas/core/indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 81329a2702cb3..46904e32f88a6 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -13,7 +13,7 @@ from pandas.core.dtypes.common import ( ensure_platform_int, is_float, is_integer, is_integer_dtype, is_iterator, - is_list_like, is_scalar, is_sequence, is_sparse, is_numeric_dtype) + is_list_like, is_numeric_dtype, is_scalar, is_sequence, is_sparse) from pandas.core.dtypes.generic import ABCDataFrame, ABCPanel, ABCSeries from pandas.core.dtypes.missing import _infer_fill_value, isna