From 5892e5a565d16291c84bd299be9e478638ebd924 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 29 Jan 2021 19:34:05 -0800 Subject: [PATCH 1/3] BUG: Series[bool].__setitem__(scalar, non_bool) --- pandas/core/dtypes/cast.py | 3 ++ pandas/tests/indexing/test_coercion.py | 29 +++++--------------- pandas/tests/series/indexing/test_setitem.py | 16 +++++++++++ 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index 0be3970159fbd..93312121a4bd2 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1910,6 +1910,9 @@ def validate_numeric_casting(dtype: np.dtype, value: Scalar) -> None: raise ValueError( f"Cannot assign {type(value).__name__} to float/integer series" ) + if 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 36cd6b0327ccd..fddac08d36690 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) + @pytest.mark.parametrize( "obj,expected,key", From 1fc2135ea4f7eec28c4581124ccf30c9b39f1cfb Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 30 Jan 2021 12:46:29 -0800 Subject: [PATCH 2/3] if -> elif --- pandas/core/dtypes/cast.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index 93312121a4bd2..2f84ea3cf43c0 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1904,13 +1904,13 @@ 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" ) - if dtype.kind == "b": + elif dtype.kind == "b": if is_scalar(value) and not is_bool(value): raise ValueError(f"Cannot assign {type(value).__name__} to bool series") From 8c27e254b9231a85a654459b99c251d95fe4fad5 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 30 Jan 2021 14:21:15 -0800 Subject: [PATCH 3/3] numpy 1.20 compat --- pandas/tests/indexing/test_loc.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index b6e4c38ef7f0e..1cd352e4e0899 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -7,7 +7,6 @@ import numpy as np import pytest -from pandas.compat import is_numpy_dev import pandas.util._test_decorators as td import pandas as pd @@ -978,7 +977,6 @@ def test_loc_setitem_empty_append_single_value(self): df.loc[0, "x"] = expected.loc[0, "x"] tm.assert_frame_equal(df, expected) - @pytest.mark.xfail(is_numpy_dev, reason="gh-35481") def test_loc_setitem_empty_append_raises(self): # GH6173, various appends to an empty dataframe @@ -992,7 +990,12 @@ def test_loc_setitem_empty_append_raises(self): with pytest.raises(KeyError, match=msg): df.loc[[0, 1], "x"] = data - msg = "cannot copy sequence with size 2 to array axis with dimension 0" + msg = "|".join( + [ + "cannot copy sequence with size 2 to array axis with dimension 0", + r"could not broadcast input array from shape \(2,\) into shape \(0,\)", + ] + ) with pytest.raises(ValueError, match=msg): df.loc[0:2, "x"] = data