Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ Groupby/resample/rolling
- 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` and :meth:`SeriesGroupBy.apply` not preserving ``_metadata`` attributes from subclassed DataFrames and Series (:issue:`62134`)
- 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`)
- Bug in :meth:`DataFrameGroupBy.cumsum` and :meth:`DataFrameGroupBy.cumprod` where ``numeric_only`` parameter was passed indirectly through kwargs instead of passing directly. (:issue:`58811`)
Expand Down
6 changes: 3 additions & 3 deletions pandas/core/groupby/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ def _wrap_applied_output(
if not self.as_index and not_indexed_same:
result = self._insert_inaxis_grouper(result)
result.index = default_index(len(result))
return result
return result.__finalize__(self.obj, method="groupby")
else:
# GH #6265 #24880
result = self.obj._constructor(
Expand All @@ -630,7 +630,7 @@ def _wrap_applied_output(
if not self.as_index:
result = self._insert_inaxis_grouper(result)
result.index = default_index(len(result))
return result
return result.__finalize__(self.obj, method="groupby")

__examples_series_doc = dedent(
"""
Expand Down Expand Up @@ -2169,7 +2169,7 @@ def _wrap_applied_output_series(
if not self.as_index:
result = self._insert_inaxis_grouper(result)

return result
return result.__finalize__(self.obj, method="groupby")

def _cython_transform(
self,
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/groupby/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,7 @@ def _concat_objects(
if isinstance(result, Series) and name is not None:
result.name = name

return result
return result.__finalize__(self.obj, method="groupby")

@final
def _set_result_index_ordered(
Expand Down
21 changes: 21 additions & 0 deletions pandas/tests/groupby/test_groupby_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,27 @@ def func2(group):
result = custom_series.groupby(custom_df["c"]).agg(func2)
tm.assert_series_equal(result, expected)

# GH#62134 - Test that apply() preserves metadata when returning DataFrames/Series
def sum_func(group):
assert isinstance(group, tm.SubclassedDataFrame)
assert hasattr(group, "testattr")
assert group.testattr == "hello"
return group.sum()

result = custom_df.groupby("c").apply(sum_func)
assert hasattr(result, "testattr"), "DataFrame apply() should preserve metadata"
assert result.testattr == "hello"

def sum_series_func(group):
assert isinstance(group, tm.SubclassedSeries)
assert hasattr(group, "testattr")
assert group.testattr == "hello"
return group.sum()

result = custom_series.groupby(custom_df["c"]).apply(sum_series_func)
assert hasattr(result, "testattr"), "Series apply() should preserve metadata"
assert result.testattr == "hello"


@pytest.mark.parametrize("obj", [DataFrame, tm.SubclassedDataFrame])
def test_groupby_resample_preserves_subclass(obj):
Expand Down
Loading