From 7a191077a5a4bc27a202dc74e3aca819fc63fc2d Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Sat, 17 Dec 2022 18:46:37 +0100 Subject: [PATCH 1/2] BUG: arithmetic ops not propagating mask when combining mask arrays and numpy --- doc/source/whatsnew/v2.0.0.rst | 1 + pandas/core/arrays/masked.py | 2 ++ pandas/tests/series/test_arithmetic.py | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index cbad169fe4d56..7553895f80cc9 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -804,6 +804,7 @@ Timezones Numeric ^^^^^^^ - Bug in :meth:`DataFrame.add` cannot apply ufunc when inputs contain mixed DataFrame type and Series type (:issue:`39853`) +- Bug in arithmetic operations on :class:`Series` not propagating mask when combining masked dtypes and numpy dtypes (:issue:`45810`, :issue:`42630`) - Bug in DataFrame reduction methods (e.g. :meth:`DataFrame.sum`) with object dtype, ``axis=1`` and ``numeric_only=False`` would not be coerced to float (:issue:`49551`) - Bug in :meth:`DataFrame.sem` and :meth:`Series.sem` where an erroneous ``TypeError`` would always raise when using data backed by an :class:`ArrowDtype` (:issue:`49759`) diff --git a/pandas/core/arrays/masked.py b/pandas/core/arrays/masked.py index 3071016bb3bda..e428cda12cc62 100644 --- a/pandas/core/arrays/masked.py +++ b/pandas/core/arrays/masked.py @@ -609,6 +609,8 @@ def _propagate_mask( if other is libmissing.NA: # GH#45421 don't alter inplace mask = mask | True + elif is_list_like(other) and len(other) == len(mask): + mask = mask | isna(other) else: mask = self._mask | mask return mask diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 3f31e355d466f..9d8cf9787ad3f 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -307,6 +307,25 @@ def test_arithmetic_with_duplicate_index(self): expected = Series(Timedelta("9 hours"), index=[2, 2, 3, 3, 4]) tm.assert_series_equal(result, expected) + def test_masked_and_non_masked_propagate_na(self): + # GH#45810 + ser1 = Series([0, np.nan], dtype="float") + ser2 = Series([0, 1], dtype="Int64") + result = ser1 * ser2 + expected = Series([0, pd.NA], dtype="Float64") + tm.assert_series_equal(result, expected) + + def test_mask_div_propagate_na_for_non_na_dtype(self): + # GH#42630 + ser1 = Series([15, pd.NA, 5, 4], dtype="Int64") + ser2 = Series([15, 5, np.nan, 4]) + result = ser1 / ser2 + expected = Series([1.0, pd.NA, pd.NA, 1.0], dtype="Float64") + tm.assert_series_equal(result, expected) + + result = ser2 / ser1 + tm.assert_series_equal(result, expected) + # ------------------------------------------------------------------ # Comparisons From b1c1182d441fa130b5b54d6b7d5150aad8c8da2d Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Sat, 17 Dec 2022 19:38:58 +0100 Subject: [PATCH 2/2] Fix mypy --- pandas/core/arrays/masked.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/arrays/masked.py b/pandas/core/arrays/masked.py index e428cda12cc62..198a6d6e285be 100644 --- a/pandas/core/arrays/masked.py +++ b/pandas/core/arrays/masked.py @@ -613,7 +613,9 @@ def _propagate_mask( mask = mask | isna(other) else: mask = self._mask | mask - return mask + # Incompatible return value type (got "Optional[ndarray[Any, dtype[bool_]]]", + # expected "ndarray[Any, dtype[bool_]]") + return mask # type: ignore[return-value] def _arith_method(self, other, op): op_name = op.__name__