Skip to content

Commit 6e09e97

Browse files
DEPR: object casting in Series logical ops in non-bool cases (#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]>
1 parent b9bfc01 commit 6e09e97

File tree

3 files changed

+24
-36
lines changed

3 files changed

+24
-36
lines changed

doc/source/whatsnew/v3.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ Removal of prior version deprecations/changes
208208
- :meth:`SeriesGroupBy.agg` no longer pins the name of the group to the input passed to the provided ``func`` (:issue:`51703`)
209209
- All arguments except ``name`` in :meth:`Index.rename` are now keyword only (:issue:`56493`)
210210
- All arguments except the first ``path``-like argument in IO writers are now keyword only (:issue:`54229`)
211+
- Disallow automatic casting to object in :class:`Series` logical operations (``&``, ``^``, ``||``) between series with mismatched indexes and dtypes other than ``object`` or ``bool`` (:issue:`52538`)
211212
- Disallow calling :meth:`Series.replace` or :meth:`DataFrame.replace` without a ``value`` and with non-dict-like ``to_replace`` (:issue:`33302`)
212213
- Disallow constructing a :class:`arrays.SparseArray` with scalar data (:issue:`53039`)
213214
- Disallow non-standard (``np.ndarray``, :class:`Index`, :class:`ExtensionArray`, or :class:`Series`) to :func:`isin`, :func:`unique`, :func:`factorize` (:issue:`52986`)

pandas/core/series.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -5859,17 +5859,12 @@ def _align_for_op(self, right, align_asobject: bool = False):
58595859
object,
58605860
np.bool_,
58615861
):
5862-
warnings.warn(
5863-
"Operation between non boolean Series with different "
5864-
"indexes will no longer return a boolean result in "
5865-
"a future version. Cast both Series to object type "
5866-
"to maintain the prior behavior.",
5867-
FutureWarning,
5868-
stacklevel=find_stack_level(),
5869-
)
5870-
# to keep original value's dtype for bool ops
5871-
left = left.astype(object)
5872-
right = right.astype(object)
5862+
pass
5863+
# GH#52538 no longer cast in these cases
5864+
else:
5865+
# to keep original value's dtype for bool ops
5866+
left = left.astype(object)
5867+
right = right.astype(object)
58735868

58745869
left, right = left.align(right)
58755870

pandas/tests/series/test_logical_ops.py

+17-25
Original file line numberDiff line numberDiff line change
@@ -233,26 +233,22 @@ def test_logical_operators_int_dtype_with_bool_dtype_and_reindex(self):
233233

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

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

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

249-
with tm.assert_produces_warning(FutureWarning):
250-
res = s_tft & s_a0b1c0
247+
res = s_tft & s_a0b1c0
251248
expected = s_tff.reindex(list("abc"))
252249
tm.assert_series_equal(res, expected)
253250

254-
with tm.assert_produces_warning(FutureWarning):
255-
res = s_tft | s_a0b1c0
251+
res = s_tft | s_a0b1c0
256252
expected = s_tft.reindex(list("abc"))
257253
tm.assert_series_equal(res, expected)
258254

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

407403
# vs non-matching
408-
with tm.assert_produces_warning(FutureWarning):
409-
result = a & Series([1], ["z"])
404+
result = a & Series([1], ["z"])
410405
expected = Series([False, False, False, False], list("abcz"))
411406
tm.assert_series_equal(result, expected)
412407

413-
with tm.assert_produces_warning(FutureWarning):
414-
result = a | Series([1], ["z"])
408+
result = a | Series([1], ["z"])
415409
expected = Series([True, True, False, False], list("abcz"))
416410
tm.assert_series_equal(result, expected)
417411

418412
# identity
419413
# we would like s[s|e] == s to hold for any e, whether empty or not
420-
with tm.assert_produces_warning(FutureWarning):
421-
for e in [
422-
empty.copy(),
423-
Series([1], ["z"]),
424-
Series(np.nan, b.index),
425-
Series(np.nan, a.index),
426-
]:
427-
result = a[a | e]
428-
tm.assert_series_equal(result, a[a])
414+
for e in [
415+
empty.copy(),
416+
Series([1], ["z"]),
417+
Series(np.nan, b.index),
418+
Series(np.nan, a.index),
419+
]:
420+
result = a[a | e]
421+
tm.assert_series_equal(result, a[a])
429422

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

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

0 commit comments

Comments
 (0)