From 18a3d772080b1bd066163905394aeddd28e05657 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 3 Sep 2025 09:40:29 -0700 Subject: [PATCH 1/2] BUG: divmod with pd.NA --- doc/source/whatsnew/v3.0.0.rst | 1 + pandas/core/arrays/masked.py | 4 ++++ pandas/tests/arrays/masked/test_arithmetic.py | 23 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index ffa65032e6aae..081be5d815035 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -951,6 +951,7 @@ Missing ^^^^^^^ - Bug in :meth:`DataFrame.fillna` and :meth:`Series.fillna` that would ignore the ``limit`` argument on :class:`.ExtensionArray` dtypes (:issue:`58001`) - Bug in :meth:`NA.__and__`, :meth:`NA.__or__` and :meth:`NA.__xor__` when operating with ``np.bool_`` objects (:issue:`58427`) +- Bug in ``divmod`` between :class:`NA` and ``Int64`` dtype objects (:issue:``62196``) - MultiIndex diff --git a/pandas/core/arrays/masked.py b/pandas/core/arrays/masked.py index ce7f288fc0238..b00e362e1309a 100644 --- a/pandas/core/arrays/masked.py +++ b/pandas/core/arrays/masked.py @@ -791,6 +791,10 @@ def _arith_method(self, other, op): # will be all-True, but since this is division, we want # to end up with floating dtype. result = result.astype(np.float64) + elif op_name in {"divmod", "rdivmod"}: + # GH#62196 + res = self._maybe_mask_result(result, mask) + return res, res.copy() else: # Make sure we do this before the "pow" mask checks # to get an expected exception message on shape mismatch. diff --git a/pandas/tests/arrays/masked/test_arithmetic.py b/pandas/tests/arrays/masked/test_arithmetic.py index ea018d2da4d26..779531d525505 100644 --- a/pandas/tests/arrays/masked/test_arithmetic.py +++ b/pandas/tests/arrays/masked/test_arithmetic.py @@ -246,3 +246,26 @@ def test_unary_op_does_not_propagate_mask(data, op): expected = result.copy(deep=True) ser[0] = None tm.assert_series_equal(result, expected) + + +@pytest.mark.parametrize("dtype", ["Int64", "Int32", "Float32", "Float64"]) +def test_divmod_pdna(dtype): + # GH#62196 + ser = pd.Series([1, 2, 3], dtype=dtype) + res = divmod(pd.NA, ser) + assert isinstance(res, tuple) and len(res) == 2 + + exp = pd.Series([pd.NA, pd.NA, pd.NA], dtype=dtype) + tm.assert_series_equal(res[0], exp) + tm.assert_series_equal(res[1], exp) + + tm.assert_series_equal(res[0], pd.NA // ser) + tm.assert_series_equal(res[1], pd.NA % ser) + + res = divmod(ser, pd.NA) + assert isinstance(res, tuple) and len(res) == 2 + tm.assert_series_equal(res[0], exp) + tm.assert_series_equal(res[1], exp) + + tm.assert_series_equal(res[0], ser // pd.NA) + tm.assert_series_equal(res[1], ser % pd.NA) From f8e87de6c232e1f4da1b448b1f968a35dcb24f7e Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 3 Sep 2025 09:41:02 -0700 Subject: [PATCH 2/2] doc typo fixup --- doc/source/whatsnew/v3.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 081be5d815035..c098a3b2443c4 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -951,7 +951,7 @@ Missing ^^^^^^^ - Bug in :meth:`DataFrame.fillna` and :meth:`Series.fillna` that would ignore the ``limit`` argument on :class:`.ExtensionArray` dtypes (:issue:`58001`) - Bug in :meth:`NA.__and__`, :meth:`NA.__or__` and :meth:`NA.__xor__` when operating with ``np.bool_`` objects (:issue:`58427`) -- Bug in ``divmod`` between :class:`NA` and ``Int64`` dtype objects (:issue:``62196``) +- Bug in ``divmod`` between :class:`NA` and ``Int64`` dtype objects (:issue:`62196`) - MultiIndex