diff --git a/doc/source/whatsnew/v1.5.2.rst b/doc/source/whatsnew/v1.5.2.rst index 446235d1656dc..540ca2b12165c 100644 --- a/doc/source/whatsnew/v1.5.2.rst +++ b/doc/source/whatsnew/v1.5.2.rst @@ -18,6 +18,7 @@ Fixed regressions - Fixed regression in :meth:`DataFrame.plot` preventing :class:`~matplotlib.colors.Colormap` instance from being passed using the ``colormap`` argument if Matplotlib 3.6+ is used (:issue:`49374`) - Fixed regression in :func:`date_range` returning an invalid set of periods for ``CustomBusinessDay`` frequency and ``start`` date with timezone (:issue:`49441`) +- Fixed performance regression in groupby operations (:issue:`49676`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 66c459b90a999..68832e9efcb6d 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -982,15 +982,6 @@ def __getattr__(self, attr: str): f"'{type(self).__name__}' object has no attribute '{attr}'" ) - def __getattribute__(self, attr: str): - # Intercept nth to allow both call and index - if attr == "nth": - return GroupByNthSelector(self) - elif attr == "nth_actual": - return super().__getattribute__("nth") - else: - return super().__getattribute__(attr) - @final def _make_wrapper(self, name: str) -> Callable: assert name in self._apply_allowlist @@ -3016,13 +3007,10 @@ def backfill(self, limit=None): return self.bfill(limit=limit) @final + @property @Substitution(name="groupby") @Substitution(see_also=_common_see_also) - def nth( - self, - n: PositionalIndexer | tuple, - dropna: Literal["any", "all", None] = None, - ) -> NDFrameT: + def nth(self) -> GroupByNthSelector: """ Take the nth row from each group if n is an int, otherwise a subset of rows. @@ -3125,6 +3113,13 @@ def nth( 1 1 2.0 4 2 5.0 """ + return GroupByNthSelector(self) + + def _nth( + self, + n: PositionalIndexer | tuple, + dropna: Literal["any", "all", None] = None, + ) -> NDFrameT: if not dropna: with self._group_selection_context(): mask = self._make_mask_from_positional_indexer(n) diff --git a/pandas/core/groupby/indexing.py b/pandas/core/groupby/indexing.py index be7b7b3369e89..750097b403f26 100644 --- a/pandas/core/groupby/indexing.py +++ b/pandas/core/groupby/indexing.py @@ -297,7 +297,7 @@ def __call__( n: PositionalIndexer | tuple, dropna: Literal["any", "all", None] = None, ) -> DataFrame | Series: - return self.groupby_object.nth_actual(n, dropna) + return self.groupby_object._nth(n, dropna) def __getitem__(self, n: PositionalIndexer | tuple) -> DataFrame | Series: - return self.groupby_object.nth_actual(n) + return self.groupby_object._nth(n)