diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index cbad169fe4d56..6a765094c551f 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -831,6 +831,7 @@ Interval Indexing ^^^^^^^^ +- Bug in :meth:`DataFrame.__setitem__` raising when indexer is a :class:`DataFrame` with ``boolean`` dtype (:issue:`47125`) - 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`) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 798ea8a61093a..728e41e6c1d3a 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4087,7 +4087,7 @@ def _setitem_frame(self, key, value): raise ValueError("Array conditional must be same shape as self") key = self._constructor(key, **self._construct_axes_dict()) - if key.size and not is_bool_dtype(key.values): + if key.size and not all(is_bool_dtype(dtype) for dtype in key.dtypes): raise TypeError( "Must pass DataFrame or 2-d ndarray with boolean values only" ) diff --git a/pandas/tests/frame/indexing/test_setitem.py b/pandas/tests/frame/indexing/test_setitem.py index e27f9fe9995ad..cb0798ffb601a 100644 --- a/pandas/tests/frame/indexing/test_setitem.py +++ b/pandas/tests/frame/indexing/test_setitem.py @@ -1125,6 +1125,19 @@ def test_loc_setitem_all_false_boolean_two_blocks(self): df.loc[indexer, ["b"]] = DataFrame({"b": [5, 6]}, index=[0, 1]) tm.assert_frame_equal(df, expected) + def test_setitem_ea_boolean_mask(self): + # GH#47125 + df = DataFrame([[-1, 2], [3, -4]]) + expected = DataFrame([[0, 2], [3, 0]]) + boolean_indexer = DataFrame( + { + 0: Series([True, False], dtype="boolean"), + 1: Series([pd.NA, True], dtype="boolean"), + } + ) + df[boolean_indexer] = 0 + tm.assert_frame_equal(df, expected) + class TestDataFrameSetitemCopyViewSemantics: def test_setitem_always_copy(self, float_frame):