Skip to content

Commit 61f5715

Browse files
jbrockmendelyehoshuadimarsky
authored andcommitted
ENH: IntegerArray bitwise operations (pandas-dev#46104)
1 parent 13b430f commit 61f5715

File tree

5 files changed

+46
-5
lines changed

5 files changed

+46
-5
lines changed

doc/source/whatsnew/v1.5.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Other enhancements
3939
- :meth:`.GroupBy.min` and :meth:`.GroupBy.max` now supports `Numba <https://numba.pydata.org/>`_ execution with the ``engine`` keyword (:issue:`45428`)
4040
- Implemented a ``bool``-dtype :class:`Index`, passing a bool-dtype array-like to ``pd.Index`` will now retain ``bool`` dtype instead of casting to ``object`` (:issue:`45061`)
4141
- Implemented a complex-dtype :class:`Index`, passing a complex-dtype array-like to ``pd.Index`` will now retain complex dtype instead of casting to ``object`` (:issue:`45845`)
42-
42+
- :class:`Series` and :class:`DataFrame` with ``IntegerDtype`` now supports bitwise operations (:issue:`34463`)
4343
-
4444

4545
.. ---------------------------------------------------------------------------

pandas/core/arrays/boolean.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,9 @@ def _logical_method(self, other, op):
375375
result, mask = ops.kleene_or(self._data, other, self._mask, mask)
376376
elif op.__name__ in {"and_", "rand_"}:
377377
result, mask = ops.kleene_and(self._data, other, self._mask, mask)
378-
elif op.__name__ in {"xor", "rxor"}:
378+
else:
379+
# i.e. xor, rxor
379380
result, mask = ops.kleene_xor(self._data, other, self._mask, mask)
380381

381-
# error: Argument 2 to "BooleanArray" has incompatible type "Optional[Any]";
382-
# expected "ndarray"
383-
return BooleanArray(result, mask) # type: ignore[arg-type]
382+
# i.e. BooleanArray
383+
return self._maybe_mask_result(result, mask)

pandas/core/arrays/masked.py

+2
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,8 @@ def _arith_method(self, other, op):
675675

676676
return self._maybe_mask_result(result, mask)
677677

678+
_logical_method = _arith_method
679+
678680
def _cmp_method(self, other, op) -> BooleanArray:
679681
from pandas.core.arrays import BooleanArray
680682

pandas/tests/arrays/floating/test_arithmetic.py

+12
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,15 @@ def test_unary_float_operators(float_ea_dtype, source, neg_target, abs_target):
218218
tm.assert_extension_array_equal(pos_result, arr)
219219
assert not tm.shares_memory(pos_result, arr)
220220
tm.assert_extension_array_equal(abs_result, abs_target)
221+
222+
223+
def test_bitwise(dtype):
224+
left = pd.array([1, None, 3, 4], dtype=dtype)
225+
right = pd.array([None, 3, 5, 4], dtype=dtype)
226+
227+
with pytest.raises(TypeError, match="unsupported operand type"):
228+
left | right
229+
with pytest.raises(TypeError, match="unsupported operand type"):
230+
left & right
231+
with pytest.raises(TypeError, match="unsupported operand type"):
232+
left ^ right

pandas/tests/arrays/integer/test_arithmetic.py

+27
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,30 @@ def test_values_multiplying_large_series_by_NA():
323323
expected = pd.Series([pd.NA] * 10001)
324324

325325
tm.assert_series_equal(result, expected)
326+
327+
328+
def test_bitwise(dtype):
329+
left = pd.array([1, None, 3, 4], dtype=dtype)
330+
right = pd.array([None, 3, 5, 4], dtype=dtype)
331+
332+
result = left | right
333+
expected = pd.array([None, None, 3 | 5, 4 | 4], dtype=dtype)
334+
tm.assert_extension_array_equal(result, expected)
335+
336+
result = left & right
337+
expected = pd.array([None, None, 3 & 5, 4 & 4], dtype=dtype)
338+
tm.assert_extension_array_equal(result, expected)
339+
340+
result = left ^ right
341+
expected = pd.array([None, None, 3 ^ 5, 4 ^ 4], dtype=dtype)
342+
tm.assert_extension_array_equal(result, expected)
343+
344+
# TODO: desired behavior when operating with boolean? defer?
345+
346+
floats = right.astype("Float64")
347+
with pytest.raises(TypeError, match="unsupported operand type"):
348+
left | floats
349+
with pytest.raises(TypeError, match="unsupported operand type"):
350+
left & floats
351+
with pytest.raises(TypeError, match="unsupported operand type"):
352+
left ^ floats

0 commit comments

Comments
 (0)