diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 005818b0779e6..e614df1a33905 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -65,6 +65,7 @@ Other enhancements - Support passing a :class:`Iterable[Hashable]` input to :meth:`DataFrame.drop_duplicates` (:issue:`59237`) - Support reading Stata 102-format (Stata 1) dta files (:issue:`58978`) - Support reading Stata 110-format (Stata 7) dta files (:issue:`47176`) +- Support skipna=True in operations on Float64 arrays with null values (:issue:`59965`) .. --------------------------------------------------------------------------- .. _whatsnew_300.notable_bug_fixes: diff --git a/pandas/core/array_algos/masked_reductions.py b/pandas/core/array_algos/masked_reductions.py index bdf88f2e9fa07..105e825712f5c 100644 --- a/pandas/core/array_algos/masked_reductions.py +++ b/pandas/core/array_algos/masked_reductions.py @@ -12,6 +12,9 @@ from pandas._libs import missing as libmissing +from pandas.core.dtypes.common import is_float_dtype + +from pandas.core.missing import isna from pandas.core.nanops import check_below_min_count if TYPE_CHECKING: @@ -57,6 +60,9 @@ def _reductions( else: return func(values, axis=axis, **kwargs) else: + if is_float_dtype(values): + mask |= isna(values) + if check_below_min_count(values.shape, mask, min_count) and ( axis is None or values.ndim == 1 ): diff --git a/pandas/tests/series/test_reductions.py b/pandas/tests/series/test_reductions.py index 86ce60b1fc12b..22b1152641c42 100644 --- a/pandas/tests/series/test_reductions.py +++ b/pandas/tests/series/test_reductions.py @@ -208,3 +208,11 @@ def test_median_with_convertible_string_raises(): df = ser.to_frame() with pytest.raises(TypeError, match=msg): df.median() + + +def test_mean_with_skipna(): + # GH#59965 skipna=True operations don't skip NaN in FloatingArrays + series1 = Series({"a": 0.0, "b": 1, "c": 1}, dtype="Float64") + series2 = Series({"a": 0.0, "b": 2, "c": 2}, dtype="Float64") + result = series1 / series2 + assert np.isclose(result.mean(skipna=True), 0.5)