From 00381b6995d51d720b562cbddeac86b78b8a04de Mon Sep 17 00:00:00 2001 From: Patrick Hoefler <61934744+phofl@users.noreply.github.com> Date: Wed, 9 Feb 2022 01:25:51 +0100 Subject: [PATCH] Backport PR #45785: Regression: Loc raising when indexing mi with one level --- doc/source/whatsnew/v1.4.1.rst | 1 + pandas/core/indexes/multi.py | 11 ++++++++++- pandas/tests/frame/indexing/test_indexing.py | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.4.1.rst b/doc/source/whatsnew/v1.4.1.rst index 78ec03b59c4fc..db6d6650d690b 100644 --- a/doc/source/whatsnew/v1.4.1.rst +++ b/doc/source/whatsnew/v1.4.1.rst @@ -16,6 +16,7 @@ Fixed regressions ~~~~~~~~~~~~~~~~~ - Regression in :meth:`Series.mask` with ``inplace=True`` and ``PeriodDtype`` and an incompatible ``other`` coercing to a common dtype instead of raising (:issue:`45546`) - Regression in :func:`.assert_frame_equal` not respecting ``check_flags=False`` (:issue:`45554`) +- Regression in :meth:`DataFrame.loc.__getitem__` raising ``ValueError`` when indexing on a :class:`MultiIndex` with one level (:issue:`45779`) - Regression in :meth:`Series.fillna` with ``downcast=False`` incorrectly downcasting ``object`` dtype (:issue:`45603`) - Regression in :func:`api.types.is_bool_dtype` raising an ``AttributeError`` when evaluating a categorical :class:`Series` (:issue:`45615`) - Regression in :meth:`DataFrame.iat` setting values leading to not propagating correctly in subsequent lookups (:issue:`45684`) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 7e59e51174b6f..dcb8437f30c9e 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2986,6 +2986,10 @@ def _get_loc_level(self, key, level: int | list[int] = 0): # different name to distinguish from maybe_droplevels def maybe_mi_droplevels(indexer, levels): + """ + If level does not exist or all levels were dropped, the exception + has to be handled outside. + """ new_index = self[indexer] for i in sorted(levels, reverse=True): @@ -3119,7 +3123,12 @@ def maybe_mi_droplevels(indexer, levels): # e.g. test_partial_string_timestamp_multiindex return indexer, self[indexer] - return indexer, maybe_mi_droplevels(indexer, [level]) + try: + result_index = maybe_mi_droplevels(indexer, [level]) + except ValueError: + result_index = self[indexer] + + return indexer, result_index def _get_level_indexer( self, key, level: int = 0, indexer: Int64Index | None = None diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 23927b3d3264e..b186a55b221c8 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1535,6 +1535,18 @@ def test_loc_iloc_setitem_non_categorical_rhs( with pytest.raises(TypeError, match=msg1): indexer(df)[key] = ["c", "c"] + def test_loc_on_multiindex_one_level(self): + # GH#45779 + df = DataFrame( + data=[[0], [1]], + index=MultiIndex.from_tuples([("a",), ("b",)], names=["first"]), + ) + expected = DataFrame( + data=[[0]], index=MultiIndex.from_tuples([("a",)], names=["first"]) + ) + result = df.loc["a"] + tm.assert_frame_equal(result, expected) + class TestDepreactedIndexers: @pytest.mark.parametrize(