From 8ccfd5ad0e529cf27f83317e1d310d5a09db666f Mon Sep 17 00:00:00 2001 From: phofl Date: Thu, 12 Nov 2020 14:05:01 +0100 Subject: [PATCH 1/6] Fix regression for loc and __setitem__ when one-dimensional tuple was given for MultiIndex --- doc/source/whatsnew/v1.2.0.rst | 1 + pandas/core/indexing.py | 5 +++-- pandas/tests/indexing/multiindex/test_loc.py | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index f751a91cecf19..c5b498cbf303a 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -470,6 +470,7 @@ Indexing - Bug in :meth:`Index.where` incorrectly casting numeric values to strings (:issue:`37591`) - Bug in :meth:`Series.loc` and :meth:`DataFrame.loc` raises when numeric label was given for object :class:`Index` although label was in :class:`Index` (:issue:`26491`) - Bug in :meth:`DataFrame.loc` returned requested key plus missing values when ``loc`` was applied to single level from :class:`MultiIndex` (:issue:`27104`) +- Fixed regression in :meth:`DataFrame.loc` and :meth:`Series.loc` for ``__setitem__`` when one-dimensional tuple was given to select from :class:`MultiIndex` (:issue:`37711`) Missing ^^^^^^^ diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index c5e331a104726..edb6379bc6a9f 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -650,11 +650,12 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None): if self.ndim != 2: return - if isinstance(key, tuple): + if isinstance(key, tuple) and not isinstance(self.obj.index, ABCMultiIndex): # key may be a tuple if we are .loc - # in that case, set key to the column part of key + # if index is not a MultiIndex, set key to column part key = key[column_axis] axis = column_axis + pass if ( axis == column_axis diff --git a/pandas/tests/indexing/multiindex/test_loc.py b/pandas/tests/indexing/multiindex/test_loc.py index 646520a9ac54f..e3fa4d5554cb1 100644 --- a/pandas/tests/indexing/multiindex/test_loc.py +++ b/pandas/tests/indexing/multiindex/test_loc.py @@ -288,6 +288,20 @@ def convert_nested_indexer(indexer_type, keys): tm.assert_series_equal(result, expected) + def test_multiindex_loc_one_dimensional_tuple(self): + # GH#37711 + mi = MultiIndex.from_tuples([("a", "A"), ("b", "A")]) + ser = Series([1, 2], index=mi) + df = ser.to_frame() + + ser.loc[("a",)] = 0 + expected = Series([0, 2], index=mi) + tm.assert_series_equal(ser, expected) + + df.loc[("a",)] = 0 + expected = expected.to_frame() + tm.assert_frame_equal(df, expected) + @pytest.mark.parametrize( "indexer, pos", From 8113cf47c01aa23bad2eacac701921c296ee8330 Mon Sep 17 00:00:00 2001 From: phofl Date: Thu, 12 Nov 2020 19:57:32 +0100 Subject: [PATCH 2/6] Use fixture to parametrize test --- pandas/tests/indexing/multiindex/test_loc.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/pandas/tests/indexing/multiindex/test_loc.py b/pandas/tests/indexing/multiindex/test_loc.py index e3fa4d5554cb1..d19bae6f028ca 100644 --- a/pandas/tests/indexing/multiindex/test_loc.py +++ b/pandas/tests/indexing/multiindex/test_loc.py @@ -288,19 +288,13 @@ def convert_nested_indexer(indexer_type, keys): tm.assert_series_equal(result, expected) - def test_multiindex_loc_one_dimensional_tuple(self): + def test_multiindex_loc_one_dimensional_tuple(self, frame_or_series): # GH#37711 mi = MultiIndex.from_tuples([("a", "A"), ("b", "A")]) - ser = Series([1, 2], index=mi) - df = ser.to_frame() - - ser.loc[("a",)] = 0 - expected = Series([0, 2], index=mi) - tm.assert_series_equal(ser, expected) - - df.loc[("a",)] = 0 - expected = expected.to_frame() - tm.assert_frame_equal(df, expected) + obj = frame_or_series([1, 2], index=mi) + obj.loc[("a",)] = 0 + expected = frame_or_series([0, 2], index=mi) + tm.assert_equal(obj, expected) @pytest.mark.parametrize( From 5944255868905c4676420b4855814190216c7148 Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 13 Nov 2020 01:38:39 +0100 Subject: [PATCH 3/6] Move whatsnew and remove pass --- doc/source/whatsnew/v1.1.5.rst | 2 +- doc/source/whatsnew/v1.2.0.rst | 1 - pandas/core/indexing.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v1.1.5.rst b/doc/source/whatsnew/v1.1.5.rst index a29ae1912e338..42cd9e3e068e0 100644 --- a/doc/source/whatsnew/v1.1.5.rst +++ b/doc/source/whatsnew/v1.1.5.rst @@ -15,7 +15,7 @@ including other versions of pandas. Fixed regressions ~~~~~~~~~~~~~~~~~ - -- +- Fixed regression in :meth:`DataFrame.loc` and :meth:`Series.loc` for ``__setitem__`` when one-dimensional tuple was given to select from :class:`MultiIndex` (:issue:`37711`) .. --------------------------------------------------------------------------- diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index c5b498cbf303a..f751a91cecf19 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -470,7 +470,6 @@ Indexing - Bug in :meth:`Index.where` incorrectly casting numeric values to strings (:issue:`37591`) - Bug in :meth:`Series.loc` and :meth:`DataFrame.loc` raises when numeric label was given for object :class:`Index` although label was in :class:`Index` (:issue:`26491`) - Bug in :meth:`DataFrame.loc` returned requested key plus missing values when ``loc`` was applied to single level from :class:`MultiIndex` (:issue:`27104`) -- Fixed regression in :meth:`DataFrame.loc` and :meth:`Series.loc` for ``__setitem__`` when one-dimensional tuple was given to select from :class:`MultiIndex` (:issue:`37711`) Missing ^^^^^^^ diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index edb6379bc6a9f..e0bf43d3a0140 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -655,7 +655,6 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None): # if index is not a MultiIndex, set key to column part key = key[column_axis] axis = column_axis - pass if ( axis == column_axis From e3f90be43b0e64ed4b1b33e6c1ee6d43ec0357b6 Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 13 Nov 2020 15:16:47 +0100 Subject: [PATCH 4/6] Add new test --- pandas/tests/indexing/multiindex/test_loc.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pandas/tests/indexing/multiindex/test_loc.py b/pandas/tests/indexing/multiindex/test_loc.py index d19bae6f028ca..b40214acd8fda 100644 --- a/pandas/tests/indexing/multiindex/test_loc.py +++ b/pandas/tests/indexing/multiindex/test_loc.py @@ -296,6 +296,15 @@ def test_multiindex_loc_one_dimensional_tuple(self, frame_or_series): expected = frame_or_series([0, 2], index=mi) tm.assert_equal(obj, expected) + @pytest.mark.parametrize("indexer", [('a',), ('a')]) + def test_multiindex_one_dimensiona_tuple_columns(self, indexer): + # GH#37711 + mi = MultiIndex.from_tuples([("a", "A"), ("b", "A")]) + obj = DataFrame([1, 2], index=mi) + obj.loc[indexer, :] = 0 + expected = DataFrame([0, 2], index=mi) + tm.assert_frame_equal(obj, expected) + @pytest.mark.parametrize( "indexer, pos", From 2f8c6ed752a587cb8392802494342d87116417bc Mon Sep 17 00:00:00 2001 From: phofl Date: Fri, 13 Nov 2020 15:34:01 +0100 Subject: [PATCH 5/6] Run black --- pandas/tests/indexing/multiindex/test_loc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexing/multiindex/test_loc.py b/pandas/tests/indexing/multiindex/test_loc.py index b40214acd8fda..6edf2e89b1aa9 100644 --- a/pandas/tests/indexing/multiindex/test_loc.py +++ b/pandas/tests/indexing/multiindex/test_loc.py @@ -296,7 +296,7 @@ def test_multiindex_loc_one_dimensional_tuple(self, frame_or_series): expected = frame_or_series([0, 2], index=mi) tm.assert_equal(obj, expected) - @pytest.mark.parametrize("indexer", [('a',), ('a')]) + @pytest.mark.parametrize("indexer", [("a",), ("a")]) def test_multiindex_one_dimensiona_tuple_columns(self, indexer): # GH#37711 mi = MultiIndex.from_tuples([("a", "A"), ("b", "A")]) From cc15da332ff7743f87b2560ae0e65c3bf970ad5d Mon Sep 17 00:00:00 2001 From: phofl Date: Sat, 14 Nov 2020 12:15:22 +0100 Subject: [PATCH 6/6] FIx typo --- pandas/tests/indexing/multiindex/test_loc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexing/multiindex/test_loc.py b/pandas/tests/indexing/multiindex/test_loc.py index 6edf2e89b1aa9..165d34180dfab 100644 --- a/pandas/tests/indexing/multiindex/test_loc.py +++ b/pandas/tests/indexing/multiindex/test_loc.py @@ -297,7 +297,7 @@ def test_multiindex_loc_one_dimensional_tuple(self, frame_or_series): tm.assert_equal(obj, expected) @pytest.mark.parametrize("indexer", [("a",), ("a")]) - def test_multiindex_one_dimensiona_tuple_columns(self, indexer): + def test_multiindex_one_dimensional_tuple_columns(self, indexer): # GH#37711 mi = MultiIndex.from_tuples([("a", "A"), ("b", "A")]) obj = DataFrame([1, 2], index=mi)