Skip to content

Commit

Permalink
DEPR: object casting in Series logical ops in non-bool cases (pandas-…
Browse files Browse the repository at this point in the history
…dev#58241)

* DEPR: object casting in Series logical ops in non-bool cases

* Update doc/source/whatsnew/v3.0.0.rst

Co-authored-by: Matthew Roeschke <[email protected]>

---------

Co-authored-by: Matthew Roeschke <[email protected]>
  • Loading branch information
jbrockmendel and mroeschke authored Apr 13, 2024
1 parent b9bfc01 commit 6e09e97
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 36 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ Removal of prior version deprecations/changes
- :meth:`SeriesGroupBy.agg` no longer pins the name of the group to the input passed to the provided ``func`` (:issue:`51703`)
- All arguments except ``name`` in :meth:`Index.rename` are now keyword only (:issue:`56493`)
- All arguments except the first ``path``-like argument in IO writers are now keyword only (:issue:`54229`)
- Disallow automatic casting to object in :class:`Series` logical operations (``&``, ``^``, ``||``) between series with mismatched indexes and dtypes other than ``object`` or ``bool`` (:issue:`52538`)
- Disallow calling :meth:`Series.replace` or :meth:`DataFrame.replace` without a ``value`` and with non-dict-like ``to_replace`` (:issue:`33302`)
- Disallow constructing a :class:`arrays.SparseArray` with scalar data (:issue:`53039`)
- Disallow non-standard (``np.ndarray``, :class:`Index`, :class:`ExtensionArray`, or :class:`Series`) to :func:`isin`, :func:`unique`, :func:`factorize` (:issue:`52986`)
Expand Down
17 changes: 6 additions & 11 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -5859,17 +5859,12 @@ def _align_for_op(self, right, align_asobject: bool = False):
object,
np.bool_,
):
warnings.warn(
"Operation between non boolean Series with different "
"indexes will no longer return a boolean result in "
"a future version. Cast both Series to object type "
"to maintain the prior behavior.",
FutureWarning,
stacklevel=find_stack_level(),
)
# to keep original value's dtype for bool ops
left = left.astype(object)
right = right.astype(object)
pass
# GH#52538 no longer cast in these cases
else:
# to keep original value's dtype for bool ops
left = left.astype(object)
right = right.astype(object)

left, right = left.align(right)

Expand Down
42 changes: 17 additions & 25 deletions pandas/tests/series/test_logical_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,26 +233,22 @@ def test_logical_operators_int_dtype_with_bool_dtype_and_reindex(self):

# s_0123 will be all false now because of reindexing like s_tft
expected = Series([False] * 7, index=[0, 1, 2, 3, "a", "b", "c"])
with tm.assert_produces_warning(FutureWarning):
result = s_tft & s_0123
result = s_tft & s_0123
tm.assert_series_equal(result, expected)

# GH 52538: Deprecate casting to object type when reindex is needed;
# GH#52538: no longer to object type when reindex is needed;
# matches DataFrame behavior
expected = Series([False] * 7, index=[0, 1, 2, 3, "a", "b", "c"])
with tm.assert_produces_warning(FutureWarning):
result = s_0123 & s_tft
tm.assert_series_equal(result, expected)
msg = r"unsupported operand type\(s\) for &: 'float' and 'bool'"
with pytest.raises(TypeError, match=msg):
s_0123 & s_tft

s_a0b1c0 = Series([1], list("b"))

with tm.assert_produces_warning(FutureWarning):
res = s_tft & s_a0b1c0
res = s_tft & s_a0b1c0
expected = s_tff.reindex(list("abc"))
tm.assert_series_equal(res, expected)

with tm.assert_produces_warning(FutureWarning):
res = s_tft | s_a0b1c0
res = s_tft | s_a0b1c0
expected = s_tft.reindex(list("abc"))
tm.assert_series_equal(res, expected)

Expand Down Expand Up @@ -405,27 +401,24 @@ def test_logical_ops_label_based(self, using_infer_string):
tm.assert_series_equal(result, expected)

# vs non-matching
with tm.assert_produces_warning(FutureWarning):
result = a & Series([1], ["z"])
result = a & Series([1], ["z"])
expected = Series([False, False, False, False], list("abcz"))
tm.assert_series_equal(result, expected)

with tm.assert_produces_warning(FutureWarning):
result = a | Series([1], ["z"])
result = a | Series([1], ["z"])
expected = Series([True, True, False, False], list("abcz"))
tm.assert_series_equal(result, expected)

# identity
# we would like s[s|e] == s to hold for any e, whether empty or not
with tm.assert_produces_warning(FutureWarning):
for e in [
empty.copy(),
Series([1], ["z"]),
Series(np.nan, b.index),
Series(np.nan, a.index),
]:
result = a[a | e]
tm.assert_series_equal(result, a[a])
for e in [
empty.copy(),
Series([1], ["z"]),
Series(np.nan, b.index),
Series(np.nan, a.index),
]:
result = a[a | e]
tm.assert_series_equal(result, a[a])

for e in [Series(["z"])]:
warn = FutureWarning if using_infer_string else None
Expand Down Expand Up @@ -519,7 +512,6 @@ def test_logical_ops_df_compat(self):
tm.assert_frame_equal(s3.to_frame() | s4.to_frame(), exp_or1.to_frame())
tm.assert_frame_equal(s4.to_frame() | s3.to_frame(), exp_or.to_frame())

@pytest.mark.xfail(reason="Will pass once #52839 deprecation is enforced")
def test_int_dtype_different_index_not_bool(self):
# GH 52500
ser1 = Series([1, 2, 3], index=[10, 11, 23], name="a")
Expand Down

0 comments on commit 6e09e97

Please sign in to comment.