From 18213174400c717bbf1196115936cf9a66982e27 Mon Sep 17 00:00:00 2001 From: phofl Date: Thu, 7 Jan 2021 20:04:40 +0100 Subject: [PATCH 1/8] Disallow DataFrame indexer for iloc setitem and getitem --- doc/source/whatsnew/v1.3.0.rst | 1 + pandas/core/indexing.py | 6 ++++++ pandas/tests/indexing/test_iloc.py | 11 +++++++++++ 3 files changed, 18 insertions(+) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 886469837d184..e6292d43dd857 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -54,6 +54,7 @@ Other enhancements - Add support for dict-like names in :class:`MultiIndex.set_names` and :class:`MultiIndex.rename` (:issue:`20421`) - :func:`pandas.read_excel` can now auto detect .xlsb files (:issue:`35416`) - :meth:`.Rolling.sum`, :meth:`.Expanding.sum`, :meth:`.Rolling.mean`, :meth:`.Expanding.mean`, :meth:`.Rolling.median`, :meth:`.Expanding.median`, :meth:`.Rolling.max`, :meth:`.Expanding.max`, :meth:`.Rolling.min`, and :meth:`.Expanding.min` now support ``Numba`` execution with the ``engine`` keyword (:issue:`38895`) +- Disallow :class:`DataFrame` indexer for ``iloc`` to make behavior consistent between :meth:`Series.__getitem__`, :meth:`DataFrame.__getitem__`, :meth:`Series.__setitem__` and :meth:`DataFrame.__setitem__` (:issue:`39004`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 60b526426d413..c3b741798b35d 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1389,6 +1389,9 @@ def _has_valid_setitem_indexer(self, indexer) -> bool: if isinstance(indexer, dict): raise IndexError("iloc cannot enlarge its target object") + if isinstance(indexer, ABCDataFrame): + raise IndexError("DataFrame indexer is not allowed for iloc") + if not isinstance(indexer, tuple): indexer = _tuplify(self.ndim, indexer) @@ -1480,6 +1483,9 @@ def _get_list_axis(self, key, axis: int): raise IndexError("positional indexers are out-of-bounds") from err def _getitem_axis(self, key, axis: int): + if isinstance(key, ABCDataFrame): + raise IndexError("DataFrame indexer is not allowed for iloc") + if isinstance(key, slice): return self._get_slice_axis(key, axis=axis) diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index 24721a370241f..337eeda1888f6 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -947,6 +947,17 @@ def test_iloc_float_raises(self, series_with_simple_index, frame_or_series): with pytest.raises(IndexError, match=_slice_iloc_msg): obj.iloc[3.0] = 0 + def test_iloc_frame_indexer(self, frame_or_series): + # GH#39004 + obj = frame_or_series([1, 2, 3]) + indexer = DataFrame([True, True, False]) + msg = "DataFrame indexer is not allowed for iloc" + with pytest.raises(IndexError, match=msg): + obj.iloc[indexer] = 1 + + with pytest.raises(IndexError, match=msg): + obj.iloc[indexer] + class TestILocSetItemDuplicateColumns: def test_iloc_setitem_scalar_duplicate_columns(self): From dfd8eba57c6a0c903d5306e99d870650af87b59b Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 8 Jan 2021 18:36:16 +0100 Subject: [PATCH 2/8] Raise future warning --- pandas/core/indexing.py | 7 ++++++- pandas/tests/indexing/test_iloc.py | 12 ++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index c3b741798b35d..a8582c413d153 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1390,7 +1390,12 @@ def _has_valid_setitem_indexer(self, indexer) -> bool: raise IndexError("iloc cannot enlarge its target object") if isinstance(indexer, ABCDataFrame): - raise IndexError("DataFrame indexer is not allowed for iloc") + warnings.warn( + "DataFrame indexer for iloc is deprecated and will be removed in " + "a future version", + FutureWarning, + stacklevel=3, + ) if not isinstance(indexer, tuple): indexer = _tuplify(self.ndim, indexer) diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index 337eeda1888f6..647c390564e2e 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -949,14 +949,14 @@ def test_iloc_float_raises(self, series_with_simple_index, frame_or_series): def test_iloc_frame_indexer(self, frame_or_series): # GH#39004 - obj = frame_or_series([1, 2, 3]) - indexer = DataFrame([True, True, False]) - msg = "DataFrame indexer is not allowed for iloc" - with pytest.raises(IndexError, match=msg): - obj.iloc[indexer] = 1 + df = DataFrame({"a": [1, 2, 3]}) + indexer = DataFrame({"a": [True, False, True]}) + with tm.assert_produces_warning(FutureWarning): + df.iloc[indexer] = 1 + msg = "DataFrame indexer is not allowed for iloc" with pytest.raises(IndexError, match=msg): - obj.iloc[indexer] + df.iloc[indexer] class TestILocSetItemDuplicateColumns: From 82446b5b292382ee90f0cdf2a127f5ddc5f5c5e2 Mon Sep 17 00:00:00 2001 From: patrick <61934744+phofl@users.noreply.github.com> Date: Fri, 8 Jan 2021 18:41:28 +0100 Subject: [PATCH 3/8] Update pandas/core/indexing.py Co-authored-by: Jeff Reback --- pandas/core/indexing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index a8582c413d153..c91a5d647b008 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1391,7 +1391,9 @@ def _has_valid_setitem_indexer(self, indexer) -> bool: if isinstance(indexer, ABCDataFrame): warnings.warn( - "DataFrame indexer for iloc is deprecated and will be removed in " + "DataFrame indexer for .iloc is deprecated and will be removed in" + "a future version.\n" + "consider using .loc with a DataFrame indexer for automatic alignment." "a future version", FutureWarning, stacklevel=3, From 637867b02a64704cc74c8b8c0374ee86024a413a Mon Sep 17 00:00:00 2001 From: patrick <61934744+phofl@users.noreply.github.com> Date: Fri, 8 Jan 2021 18:42:44 +0100 Subject: [PATCH 4/8] Update pandas/core/indexing.py Co-authored-by: Jeff Reback --- 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 c91a5d647b008..2662da0fcb6a8 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1491,7 +1491,7 @@ def _get_list_axis(self, key, axis: int): def _getitem_axis(self, key, axis: int): if isinstance(key, ABCDataFrame): - raise IndexError("DataFrame indexer is not allowed for iloc") + raise IndexError("DataFrame indexer is not allowed for .iloc\nConsider using .loc for automatic alignment.") if isinstance(key, slice): return self._get_slice_axis(key, axis=axis) From a735e1ccef8a3035653e8cb3872014e29dba96b2 Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 8 Jan 2021 18:44:11 +0100 Subject: [PATCH 5/8] Fix black issues --- pandas/core/indexing.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 2662da0fcb6a8..84fbd4abd2094 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1393,7 +1393,7 @@ def _has_valid_setitem_indexer(self, indexer) -> bool: warnings.warn( "DataFrame indexer for .iloc is deprecated and will be removed in" "a future version.\n" - "consider using .loc with a DataFrame indexer for automatic alignment." + "consider using .loc with a DataFrame indexer for automatic alignment." "a future version", FutureWarning, stacklevel=3, @@ -1491,7 +1491,10 @@ def _get_list_axis(self, key, axis: int): def _getitem_axis(self, key, axis: int): if isinstance(key, ABCDataFrame): - raise IndexError("DataFrame indexer is not allowed for .iloc\nConsider using .loc for automatic alignment.") + raise IndexError( + "DataFrame indexer is not allowed for .iloc\n" + "Consider using .loc for automatic alignment." + ) if isinstance(key, slice): return self._get_slice_axis(key, axis=axis) From d9b7295307ec955eb63f91deaf1c79a614202244 Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 8 Jan 2021 18:45:24 +0100 Subject: [PATCH 6/8] Change whatsnew --- doc/source/whatsnew/v1.3.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index e6292d43dd857..34a43ed3b2662 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -54,7 +54,7 @@ Other enhancements - Add support for dict-like names in :class:`MultiIndex.set_names` and :class:`MultiIndex.rename` (:issue:`20421`) - :func:`pandas.read_excel` can now auto detect .xlsb files (:issue:`35416`) - :meth:`.Rolling.sum`, :meth:`.Expanding.sum`, :meth:`.Rolling.mean`, :meth:`.Expanding.mean`, :meth:`.Rolling.median`, :meth:`.Expanding.median`, :meth:`.Rolling.max`, :meth:`.Expanding.max`, :meth:`.Rolling.min`, and :meth:`.Expanding.min` now support ``Numba`` execution with the ``engine`` keyword (:issue:`38895`) -- Disallow :class:`DataFrame` indexer for ``iloc`` to make behavior consistent between :meth:`Series.__getitem__`, :meth:`DataFrame.__getitem__`, :meth:`Series.__setitem__` and :meth:`DataFrame.__setitem__` (:issue:`39004`) +- Disallow :class:`DataFrame` indexer for ``iloc`` for :meth:`Series.__getitem__` and :meth:`DataFrame.__getitem__`, (:issue:`39004`) .. --------------------------------------------------------------------------- @@ -164,7 +164,7 @@ Deprecations - Deprecated comparison of :class:`Timestamp` object with ``datetime.date`` objects. Instead of e.g. ``ts <= mydate`` use ``ts <= pd.Timestamp(mydate)`` or ``ts.date() <= mydate`` (:issue:`36131`) - Deprecated :attr:`Rolling.win_type` returning ``"freq"`` (:issue:`38963`) - Deprecated :attr:`Rolling.is_datetimelike` (:issue:`38963`) -- +- Deprecated :class:`DataFrame` indexer for :meth:`Series.__setitem__` and :meth:`DataFrame.__setitem__` (:issue:`39004`) .. --------------------------------------------------------------------------- From 82d4cfaccc9928443b3229f927a0cf3e6b149e36 Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 8 Jan 2021 19:15:44 +0100 Subject: [PATCH 7/8] Fix test --- pandas/tests/indexing/test_iloc.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index 647c390564e2e..c1fa4ef7824be 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -947,14 +947,17 @@ def test_iloc_float_raises(self, series_with_simple_index, frame_or_series): with pytest.raises(IndexError, match=_slice_iloc_msg): obj.iloc[3.0] = 0 - def test_iloc_frame_indexer(self, frame_or_series): + def test_iloc_frame_indexer(self): # GH#39004 df = DataFrame({"a": [1, 2, 3]}) indexer = DataFrame({"a": [True, False, True]}) with tm.assert_produces_warning(FutureWarning): df.iloc[indexer] = 1 - msg = "DataFrame indexer is not allowed for iloc" + msg = ( + "DataFrame indexer is not allowed for .iloc\n" + "Consider using .loc for automatic alignment." + ) with pytest.raises(IndexError, match=msg): df.iloc[indexer] From 4da631dc209bb8d2182ccb51340041a7915810e8 Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 8 Jan 2021 21:34:05 +0100 Subject: [PATCH 8/8] Remove repeat --- pandas/core/indexing.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 011ec67e8e4fb..97d298aeae19a 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1395,8 +1395,7 @@ def _has_valid_setitem_indexer(self, indexer) -> bool: warnings.warn( "DataFrame indexer for .iloc is deprecated and will be removed in" "a future version.\n" - "consider using .loc with a DataFrame indexer for automatic alignment." - "a future version", + "consider using .loc with a DataFrame indexer for automatic alignment.", FutureWarning, stacklevel=3, )