From 8abd0586edc479de92380c9bcc3685fb1aecf736 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Mon, 22 Aug 2022 21:55:00 +0200 Subject: [PATCH] Backport PR #48149: REGR: fix calling numpy bitwise ufunc with Index objects --- doc/source/whatsnew/v1.4.4.rst | 1 + pandas/core/indexes/base.py | 18 +++++++++++++----- pandas/tests/indexes/test_numpy_compat.py | 13 +++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v1.4.4.rst b/doc/source/whatsnew/v1.4.4.rst index 25db2ef7253d9..a8aafbd2fac21 100644 --- a/doc/source/whatsnew/v1.4.4.rst +++ b/doc/source/whatsnew/v1.4.4.rst @@ -16,6 +16,7 @@ Fixed regressions ~~~~~~~~~~~~~~~~~ - Fixed regression in taking NULL :class:`objects` from a :class:`DataFrame` causing a segmentation violation. These NULL values are created by :meth:`numpy.empty_like` (:issue:`46848`) - Fixed regression in :func:`concat` materializing :class:`Index` during sorting even if :class:`Index` was already sorted (:issue:`47501`) +- Fixed regression in calling bitwise numpy ufuncs (for example, ``np.bitwise_and``) on Index objects (:issue:`46769`) - Fixed regression in :func:`cut` using a ``datetime64`` IntervalIndex as bins (:issue:`46218`) - Fixed regression in :meth:`DataFrame.select_dtypes` where ``include="number"`` included :class:`BooleanDtype` (:issue:`46870`) - Fixed regression in :meth:`DataFrame.loc` not updating the cache correctly after values were set (:issue:`47867`) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 5707ba1e025a4..cf57ae1d1ca4d 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -897,11 +897,19 @@ def __array_ufunc__(self, ufunc: np.ufunc, method: str_t, *inputs, **kwargs): if any(isinstance(other, (ABCSeries, ABCDataFrame)) for other in inputs): return NotImplemented - result = arraylike.maybe_dispatch_ufunc_to_dunder_op( - self, ufunc, method, *inputs, **kwargs - ) - if result is not NotImplemented: - return result + # TODO(2.0) the 'and', 'or' and 'xor' dunder methods are currently set + # operations and not logical operations, so don't dispatch + # This is deprecated, so this full 'if' clause can be removed once + # deprecation is enforced in 2.0 + if not ( + method == "__call__" + and ufunc in (np.bitwise_and, np.bitwise_or, np.bitwise_xor) + ): + result = arraylike.maybe_dispatch_ufunc_to_dunder_op( + self, ufunc, method, *inputs, **kwargs + ) + if result is not NotImplemented: + return result if "out" in kwargs: # e.g. test_dti_isub_tdi diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py index 3fad8033410c8..9711fad3f9e1a 100644 --- a/pandas/tests/indexes/test_numpy_compat.py +++ b/pandas/tests/indexes/test_numpy_compat.py @@ -146,3 +146,16 @@ def test_numpy_ufuncs_reductions(index, func, request): assert isna(expected) else: assert result == expected + + +@pytest.mark.parametrize("func", [np.bitwise_and, np.bitwise_or, np.bitwise_xor]) +def test_numpy_ufuncs_bitwise(func): + # https://github.com/pandas-dev/pandas/issues/46769 + idx1 = Index([1, 2, 3, 4], dtype="int64") + idx2 = Index([3, 4, 5, 6], dtype="int64") + + with tm.assert_produces_warning(None): + result = func(idx1, idx2) + + expected = Index(func(idx1.values, idx2.values)) + tm.assert_index_equal(result, expected)