From 34e6adb1c0f6b61c9e5f31177accaa1cdac03eb3 Mon Sep 17 00:00:00 2001 From: arthurlw Date: Wed, 28 May 2025 17:40:04 +0700 Subject: [PATCH 1/4] Wrapped return object --- pandas/core/groupby/generic.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index b520ad69aae96..49b80337c700e 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -504,11 +504,13 @@ def aggregate(self, func=None, *args, engine=None, engine_kwargs=None, **kwargs) # inference. We default to using the existing dtype. # xref GH#51445 obj = self._obj_with_exclusions - return self.obj._constructor( - [], - name=self.obj.name, - index=self._grouper.result_index, - dtype=obj.dtype, + return self._wrap_aggregated_output( + self.obj._constructor( + [], + name=self.obj.name, + index=self._grouper.result_index, + dtype=obj.dtype, + ) ) return self._python_agg_general(func, *args, **kwargs) From 1b4a149fb5932c3415ed30ab83ac270f5df1fa6f Mon Sep 17 00:00:00 2001 From: arthurlw Date: Wed, 28 May 2025 20:20:13 +0700 Subject: [PATCH 2/4] Added tests --- pandas/tests/groupby/aggregate/test_aggregate.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index b7e6e55739c17..0b96f0a7c9bd7 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -1807,3 +1807,17 @@ def test_groupby_aggregation_func_list_multi_index_duplicate_columns(): index=Index(["level1.1", "level1.2"]), ) tm.assert_frame_equal(result, expected) + +def test_groupby_aggregate_empty_builtin_sum(): + df = pd.DataFrame(columns=["Group", "Data"]) + result = df.groupby(["Group"], as_index=False)["Data"].agg("sum") + expected = pd.DataFrame(columns=["Group", "Data"]) + tm.assert_frame_equal(result, expected) + +def test_groupby_aggregate_empty_udf(): + def func(x): + return sum(x) + df = pd.DataFrame(columns=["Group", "Data"]) + result = df.groupby(["Group"], as_index=False)["Data"].agg(func) + expected = pd.DataFrame(columns=["Group", "Data"]) + tm.assert_frame_equal(result, expected) \ No newline at end of file From ab16ad10feae0e45800004fc3aa4c75e7b18040e Mon Sep 17 00:00:00 2001 From: arthurlw Date: Wed, 28 May 2025 20:23:23 +0700 Subject: [PATCH 3/4] Whatsnew --- doc/source/whatsnew/v3.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 76f6eab97c4eb..9e374db2fc856 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -823,6 +823,7 @@ Groupby/resample/rolling - Bug in :meth:`DataFrame.resample` and :meth:`Series.resample` were not keeping the index name when the index had :class:`ArrowDtype` timestamp dtype (:issue:`61222`) - Bug in :meth:`DataFrame.resample` changing index type to :class:`MultiIndex` when the dataframe is empty and using an upsample method (:issue:`55572`) - 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`) +- Bug in :meth:`DataFrameGroupBy.agg` where applying a user-defined function to an empty DataFrame returned a Series instead of an empty DataFrame. (:issue:`61503`) - 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`) - 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`) - Bug in :meth:`DataFrameGroupBy.apply` with ``as_index=False`` that was returning :class:`MultiIndex` instead of returning :class:`Index`. (:issue:`58291`) From a3180bfcd81b9d3e3787f6abeefc15b67e5acdde Mon Sep 17 00:00:00 2001 From: arthurlw Date: Fri, 30 May 2025 15:58:18 +0700 Subject: [PATCH 4/4] pre-commit --- pandas/tests/groupby/aggregate/test_aggregate.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index 0b96f0a7c9bd7..4f6c27bd327cb 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -1808,16 +1808,19 @@ def test_groupby_aggregation_func_list_multi_index_duplicate_columns(): ) tm.assert_frame_equal(result, expected) + def test_groupby_aggregate_empty_builtin_sum(): - df = pd.DataFrame(columns=["Group", "Data"]) + df = DataFrame(columns=["Group", "Data"]) result = df.groupby(["Group"], as_index=False)["Data"].agg("sum") - expected = pd.DataFrame(columns=["Group", "Data"]) + expected = DataFrame(columns=["Group", "Data"]) tm.assert_frame_equal(result, expected) + def test_groupby_aggregate_empty_udf(): def func(x): return sum(x) - df = pd.DataFrame(columns=["Group", "Data"]) + + df = DataFrame(columns=["Group", "Data"]) result = df.groupby(["Group"], as_index=False)["Data"].agg(func) - expected = pd.DataFrame(columns=["Group", "Data"]) - tm.assert_frame_equal(result, expected) \ No newline at end of file + expected = DataFrame(columns=["Group", "Data"]) + tm.assert_frame_equal(result, expected)