From 3d247671df05e1d29f0878751b8ba940d9cafd65 Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Fri, 18 Nov 2022 11:33:38 +0100 Subject: [PATCH] BUG: loc raising error for MultiIndex with bool indexer --- doc/source/whatsnew/v2.0.0.rst | 1 + pandas/core/indexing.py | 7 +++++-- pandas/tests/frame/indexing/test_indexing.py | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 1ad66b07a9d08..620d16ccf8b41 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -661,6 +661,7 @@ Indexing ^^^^^^^^ - Bug in :meth:`DataFrame.reindex` filling with wrong values when indexing columns and index for ``uint`` dtypes (:issue:`48184`) - Bug in :meth:`DataFrame.loc` coercing dtypes when setting values with a list indexer (:issue:`49159`) +- Bug in :meth:`DataFrame.loc` raising ``ValueError`` with ``bool`` indexer and :class:`MultiIndex` (:issue:`47687`) - Bug in :meth:`DataFrame.__setitem__` raising ``ValueError`` when right hand side is :class:`DataFrame` with :class:`MultiIndex` columns (:issue:`49121`) - Bug in :meth:`DataFrame.reindex` casting dtype to ``object`` when :class:`DataFrame` has single extension array column when re-indexing ``columns`` and ``index`` (:issue:`48190`) - Bug in :func:`~DataFrame.describe` when formatting percentiles in the resulting index showed more decimals than needed (:issue:`46362`) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index b6fd298a2d41a..929a1c4e30a5f 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1115,9 +1115,12 @@ def _validate_key(self, key, axis: Axis): # slice of labels (where start-end in labels) # slice of integers (only if in the labels) # boolean not in slice and with boolean index + ax = self.obj._get_axis(axis) if isinstance(key, bool) and not ( - is_bool_dtype(self.obj._get_axis(axis)) - or self.obj._get_axis(axis).dtype.name == "boolean" + is_bool_dtype(ax) + or ax.dtype.name == "boolean" + or isinstance(ax, MultiIndex) + and is_bool_dtype(ax.get_level_values(0)) ): raise KeyError( f"{key}: boolean label can not be used without a boolean index" diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 4e4d0590830de..be67ce50a0634 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1436,6 +1436,24 @@ def test_loc_rhs_empty_warning(self): df.loc[:, "a"] = rhs tm.assert_frame_equal(df, expected) + @pytest.mark.parametrize("indexer", [True, (True,)]) + @pytest.mark.parametrize("dtype", [bool, "boolean"]) + def test_loc_bool_multiindex(self, dtype, indexer): + # GH#47687 + midx = MultiIndex.from_arrays( + [ + Series([True, True, False, False], dtype=dtype), + Series([True, False, True, False], dtype=dtype), + ], + names=["a", "b"], + ) + df = DataFrame({"c": [1, 2, 3, 4]}, index=midx) + result = df.loc[indexer] + expected = DataFrame( + {"c": [1, 2]}, index=Index([True, False], name="b", dtype=dtype) + ) + tm.assert_frame_equal(result, expected) + class TestDataFrameIndexingUInt64: def test_setitem(self, uint64_frame):