diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 8d3ac0e396430..2f9031bcea070 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -748,6 +748,7 @@ Indexing - Bug in :meth:`DataFrame.loc` with inconsistent behavior of loc-set with 2 given indexes to Series (:issue:`59933`) - Bug in :meth:`Index.get_indexer` and similar methods when ``NaN`` is located at or after position 128 (:issue:`58924`) - Bug in :meth:`MultiIndex.insert` when a new value inserted to a datetime-like level gets cast to ``NaT`` and fails indexing (:issue:`60388`) +- Bug in :meth:`Series.__setitem__` when assigning boolean series with boolean indexer will raise ``LossySetitemError`` (:issue:`57338`) - Bug in printing :attr:`Index.names` and :attr:`MultiIndex.levels` would not escape single quotes (:issue:`60190`) - Bug in reindexing of :class:`DataFrame` with :class:`PeriodDtype` columns in case of consolidated block (:issue:`60980`, :issue:`60273`) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index dae04ba6244d4..47e52f36ad121 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1926,6 +1926,10 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any: # i.e. there are pd.NA elements raise LossySetitemError return element + # GH 57338 check boolean array set as object type + if tipo.kind == "O" and isinstance(element, np.ndarray): + if lib.is_bool_array(element): + return element.astype("bool") raise LossySetitemError if lib.is_bool(element): return element diff --git a/pandas/tests/dtypes/cast/test_can_hold_element.py b/pandas/tests/dtypes/cast/test_can_hold_element.py index 3b7d76ead119a..5ef021753b843 100644 --- a/pandas/tests/dtypes/cast/test_can_hold_element.py +++ b/pandas/tests/dtypes/cast/test_can_hold_element.py @@ -77,3 +77,22 @@ def test_can_hold_element_int8_int(): assert not can_hold_element(arr, np.uint32(element)) assert not can_hold_element(arr, np.int64(element)) assert not can_hold_element(arr, np.uint64(element)) + + +def test_can_hold_element_bool(): + arr = np.array([], dtype=bool) + + element = True + assert can_hold_element(arr, element) + assert can_hold_element(arr, np.array([element])) + assert can_hold_element(arr, np.array([element], dtype=object)) + + element = 1 + assert not can_hold_element(arr, element) + assert not can_hold_element(arr, np.array([element])) + assert not can_hold_element(arr, np.array([element], dtype=object)) + + element = np.nan + assert not can_hold_element(arr, element) + assert not can_hold_element(arr, np.array([element])) + assert not can_hold_element(arr, np.array([element], dtype=object)) diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py index 964f0b90b3c41..ae3b0d10214e3 100644 --- a/pandas/tests/series/indexing/test_setitem.py +++ b/pandas/tests/series/indexing/test_setitem.py @@ -1838,3 +1838,13 @@ def test_setitem_empty_mask_dont_upcast_dt64(): ser.mask(mask, "foo", inplace=True) assert ser.dtype == dti.dtype # no-op -> dont upcast tm.assert_series_equal(ser, orig) + + +def test_setitem_bool_dtype_with_boolean_indexer(): + # GH 57338 + s1 = Series([True, True, True], dtype=bool) + s2 = Series([False, False, False], dtype=bool) + condition = [False, True, False] + s1[condition] = s2[condition] + expected = Series([True, False, True], dtype=bool) + tm.assert_series_equal(s1, expected)