diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index 0be3970159fbd..2f84ea3cf43c0 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1904,12 +1904,15 @@ def validate_numeric_casting(dtype: np.dtype, value: Scalar) -> None: ): raise ValueError("Cannot assign nan to integer series") - if dtype.kind in ["i", "u", "f", "c"]: + elif dtype.kind in ["i", "u", "f", "c"]: if is_bool(value) or isinstance(value, np.timedelta64): # numpy will cast td64 to integer if we're not careful raise ValueError( f"Cannot assign {type(value).__name__} to float/integer series" ) + elif dtype.kind == "b": + if is_scalar(value) and not is_bool(value): + raise ValueError(f"Cannot assign {type(value).__name__} to bool series") def can_hold_element(dtype: np.dtype, element: Any) -> bool: diff --git a/pandas/tests/indexing/test_coercion.py b/pandas/tests/indexing/test_coercion.py index 6c8b1622e76aa..69b9e63d7e215 100644 --- a/pandas/tests/indexing/test_coercion.py +++ b/pandas/tests/indexing/test_coercion.py @@ -161,34 +161,19 @@ def test_setitem_series_complex128(self, val, exp_dtype): @pytest.mark.parametrize( "val,exp_dtype", [ - (1, np.int64), - (3, np.int64), - (1.1, np.float64), - (1 + 1j, np.complex128), + (1, object), + ("3", object), + (3, object), + (1.1, object), + (1 + 1j, object), (True, np.bool_), ], ) - def test_setitem_series_bool(self, val, exp_dtype, request): + def test_setitem_series_bool(self, val, exp_dtype): obj = pd.Series([True, False, True, False]) assert obj.dtype == np.bool_ - mark = None - if exp_dtype is np.int64: - exp = pd.Series([True, True, True, False]) - self._assert_setitem_series_conversion(obj, val, exp, np.bool_) - mark = pytest.mark.xfail(reason="TODO_GH12747 The result must be int") - elif exp_dtype is np.float64: - exp = pd.Series([True, True, True, False]) - self._assert_setitem_series_conversion(obj, val, exp, np.bool_) - mark = pytest.mark.xfail(reason="TODO_GH12747 The result must be float") - elif exp_dtype is np.complex128: - exp = pd.Series([True, True, True, False]) - self._assert_setitem_series_conversion(obj, val, exp, np.bool_) - mark = pytest.mark.xfail(reason="TODO_GH12747 The result must be complex") - if mark is not None: - request.node.add_marker(mark) - - exp = pd.Series([True, val, True, False]) + exp = pd.Series([True, val, True, False], dtype=exp_dtype) self._assert_setitem_series_conversion(obj, val, exp, exp_dtype) @pytest.mark.parametrize( diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py index 9ace404930876..8c40ef6261d19 100644 --- a/pandas/tests/series/indexing/test_setitem.py +++ b/pandas/tests/series/indexing/test_setitem.py @@ -281,6 +281,22 @@ def test_setitem_dt64_into_int_series(self, dtype): ser[:-1] = np.array([val, val]) tm.assert_series_equal(ser, expected) + @pytest.mark.parametrize("unique", [True, False]) + @pytest.mark.parametrize("val", [3, 3.0, "3"], ids=type) + def test_setitem_non_bool_into_bool(self, val, indexer_sli, unique): + # dont cast these 3-like values to bool + ser = Series([True, False]) + if not unique: + ser.index = [1, 1] + + indexer_sli(ser)[1] = val + assert type(ser.iloc[1]) == type(val) + + expected = Series([True, val], dtype=object, index=ser.index) + if not unique and indexer_sli is not tm.iloc: + expected = Series([val, val], dtype=object, index=[1, 1]) + tm.assert_series_equal(ser, expected) + class SetitemCastingEquivalents: """