Skip to content

Commit 45eb9af

Browse files
committed
Fix behavior for DataFrame.sum and Series.sum
1 parent fba9f78 commit 45eb9af

File tree

5 files changed

+38
-2
lines changed

5 files changed

+38
-2
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,13 +750,13 @@ Groupby/resample/rolling
750750
- Bug in :meth:`.DataFrameGroupBy.quantile` when ``interpolation="nearest"`` is inconsistent with :meth:`DataFrame.quantile` (:issue:`47942`)
751751
- Bug in :meth:`.Resampler.interpolate` on a :class:`DataFrame` with non-uniform sampling and/or indices not aligning with the resulting resampled index would result in wrong interpolation (:issue:`21351`)
752752
- Bug in :meth:`DataFrame.ewm` and :meth:`Series.ewm` when passed ``times`` and aggregation functions other than mean (:issue:`51695`)
753+
- Bug in :meth:`DataFrame.sum`, :meth:`Series.sum`, :meth:`DataFrameGroupBy.sum` and :math:`SeriesGroupBy.sum` where in case of all-nan values for object dtype the result is incorrectly set to 0 instead of ``nan``. (:issue:`60229`)
753754
- Bug in :meth:`DataFrameGroupBy.agg` that raises ``AttributeError`` when there is dictionary input and duplicated columns, instead of returning a DataFrame with the aggregation of all duplicate columns. (:issue:`55041`)
754755
- Bug in :meth:`DataFrameGroupBy.apply` and :meth:`SeriesGroupBy.apply` for empty data frame with ``group_keys=False`` still creating output index using group keys. (:issue:`60471`)
755756
- Bug in :meth:`DataFrameGroupBy.apply` that was returning a completely empty DataFrame when all return values of ``func`` were ``None`` instead of returning an empty DataFrame with the original columns and dtypes. (:issue:`57775`)
756757
- Bug in :meth:`DataFrameGroupBy.apply` with ``as_index=False`` that was returning :class:`MultiIndex` instead of returning :class:`Index`. (:issue:`58291`)
757758
- Bug in :meth:`DataFrameGroupBy.cumsum` and :meth:`DataFrameGroupBy.cumprod` where ``numeric_only`` parameter was passed indirectly through kwargs instead of passing directly. (:issue:`58811`)
758759
- Bug in :meth:`DataFrameGroupBy.cumsum` where it did not return the correct dtype when the label contained ``None``. (:issue:`58811`)
759-
- Bug in :meth:`DataFrameGroupBy.sum` and :math:`SeriesGroupBy.sum` where in case of all-nan values for object dtype the result is incorrectly set to 0 instead of ``None``. (:issue:`58811`)
760760
- Bug in :meth:`DataFrameGroupby.transform` and :meth:`SeriesGroupby.transform` with a reducer and ``observed=False`` that coerces dtype to float when there are unobserved categories. (:issue:`55326`)
761761
- Bug in :meth:`Rolling.apply` for ``method="table"`` where column order was not being respected due to the columns getting sorted by default. (:issue:`59666`)
762762
- Bug in :meth:`Rolling.apply` where the applied function could be called on fewer than ``min_period`` periods if ``method="table"``. (:issue:`58868`)

pandas/core/nanops.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,13 @@ def nansum(
638638
the_sum = values.sum(axis, dtype=dtype_sum)
639639
the_sum = _maybe_null_out(the_sum, axis, mask, values.shape, min_count=min_count)
640640

641+
if dtype.kind == "O" and skipna and min_count == 0:
642+
# GH#60229 For object dtype, sum of all-NA array should be nan
643+
if isinstance(the_sum, np.ndarray):
644+
the_sum[mask.sum(axis=axis) == mask.shape[axis]] = np.nan
645+
elif mask.all():
646+
the_sum = np.nan
647+
641648
return the_sum
642649

643650

pandas/tests/frame/test_arithmetic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ def test_frame_single_columns_object_sum_axis_1():
11861186
}
11871187
df = DataFrame(data)
11881188
result = df.sum(axis=1)
1189-
expected = Series(["A", 1.2, 0])
1189+
expected = Series(["A", 1.2, np.nan])
11901190
tm.assert_series_equal(result, expected)
11911191

11921192

pandas/tests/frame/test_reductions.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,25 @@ def test_stat_operators_attempt_obj_array(self, method, df, axis):
420420
assert df.values.dtype == np.object_
421421
result = getattr(df, method)(axis=axis)
422422
expected = getattr(df.astype("f8"), method)(axis=axis).astype(object)
423+
if method == "sum":
424+
# GH#60229 in case of all-NA object array, sum should be nan
425+
expected[df.isna().all(axis=axis)] = np.nan
423426
if axis in [1, "columns"] and method in ["min", "max"]:
424427
expected[expected.isna()] = None
425428
tm.assert_series_equal(result, expected)
426429

430+
def test_object_sum_allna(self):
431+
# GH#60229
432+
df = DataFrame({"a": [np.nan] * 5, "b": [pd.NA] * 5}, dtype=object)
433+
434+
result = df.sum(axis=0, skipna=True)
435+
expected = Series([np.nan, np.nan], index=["a", "b"], dtype=object)
436+
tm.assert_series_equal(result, expected)
437+
438+
result = df.sum(axis=0, skipna=False)
439+
expected = Series([np.nan, pd.NA], index=["a", "b"], dtype=object)
440+
tm.assert_series_equal(result, expected)
441+
427442
@pytest.mark.parametrize("op", ["mean", "std", "var", "skew", "kurt", "sem"])
428443
def test_mixed_ops(self, op):
429444
# GH#16116

pandas/tests/series/test_reductions.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@ def test_prod_numpy16_bug():
111111
assert not isinstance(result, Series)
112112

113113

114+
@pytest.mark.parametrize("nan_val", [np.nan, pd.NA])
115+
def test_object_sum_allna(nan_val):
116+
# GH#60229
117+
ser = Series([nan_val] * 5, dtype=object)
118+
119+
result = ser.sum(axis=0, skipna=True)
120+
expected = np.nan
121+
tm.assert_equal(result, expected)
122+
123+
result = ser.sum(axis=0, skipna=False)
124+
expected = nan_val
125+
tm.assert_equal(result, expected)
126+
127+
114128
@pytest.mark.parametrize("func", [np.any, np.all])
115129
@pytest.mark.parametrize("kwargs", [{"keepdims": True}, {"out": object()}])
116130
def test_validate_any_all_out_keepdims_raises(kwargs, func):

0 commit comments

Comments
 (0)