diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 532881fee0892..d7888c6c75454 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -786,6 +786,9 @@ Deprecations - Deprecated calling ``float`` or ``int`` on a single element :class:`Series` to return a ``float`` or ``int`` respectively. Extract the element before calling ``float`` or ``int`` instead (:issue:`51101`) - Deprecated :meth:`Grouper.groups`, use :meth:`Groupby.groups` instead (:issue:`51182`) - Deprecated :meth:`Grouper.grouper`, use :meth:`Groupby.grouper` instead (:issue:`51182`) +- Deprecated :meth:`Grouper.obj`, use :meth:`Groupby.obj` instead (:issue:`51206`) +- Deprecated :meth:`Grouper.indexer`, use :meth:`Resampler.indexer` instead (:issue:`51206`) +- Deprecated :meth:`Grouper.ax`, use :meth:`Resampler.ax` instead (:issue:`51206`) - Deprecated :meth:`Series.pad` in favor of :meth:`Series.ffill` (:issue:`33396`) - Deprecated :meth:`Series.backfill` in favor of :meth:`Series.bfill` (:issue:`33396`) - Deprecated :meth:`DataFrame.pad` in favor of :meth:`DataFrame.ffill` (:issue:`33396`) diff --git a/pandas/core/groupby/grouper.py b/pandas/core/groupby/grouper.py index ee0ff93e0b538..5f69d297f4426 100644 --- a/pandas/core/groupby/grouper.py +++ b/pandas/core/groupby/grouper.py @@ -270,21 +270,13 @@ def __init__( self.dropna = dropna self._grouper_deprecated = None + self._indexer_deprecated = None + self._obj_deprecated = None self._gpr_index = None - self.obj = None - self.indexer = None self.binner = None self._grouper = None self._indexer = None - @final - @property - def ax(self) -> Index: - index = self._gpr_index - if index is None: - raise ValueError("_set_grouper must be called before ax is accessed") - return index - def _get_grouper( self, obj: NDFrameT, validate: bool = True ) -> tuple[ops.BaseGrouper, NDFrameT]: @@ -299,7 +291,7 @@ def _get_grouper( ------- a tuple of grouper, obj (possibly sorted) """ - obj, _ = self._set_grouper(obj) + obj, _, _ = self._set_grouper(obj) grouper, _, obj = get_grouper( obj, [self.key], @@ -335,9 +327,12 @@ def _set_grouper( ------- NDFrame Index + np.ndarray[np.intp] | None """ assert obj is not None + indexer = None + if self.key is not None and self.level is not None: raise ValueError("The Grouper cannot specify both a key and a level!") @@ -345,7 +340,7 @@ def _set_grouper( if self._grouper is None: # TODO: What are we assuming about subsequent calls? self._grouper = gpr_index - self._indexer = self.indexer + self._indexer = self._indexer_deprecated # the key must be a valid info item if self.key is not None: @@ -387,7 +382,7 @@ def _set_grouper( if (self.sort or sort) and not ax.is_monotonic_increasing: # use stable sort to support first, last, nth # TODO: why does putting na_position="first" fix datetimelike cases? - indexer = self.indexer = ax.array.argsort( + indexer = self._indexer_deprecated = ax.array.argsort( kind="mergesort", na_position="first" ) ax = ax.take(indexer) @@ -395,9 +390,45 @@ def _set_grouper( # error: Incompatible types in assignment (expression has type # "NDFrameT", variable has type "None") - self.obj = obj # type: ignore[assignment] + self._obj_deprecated = obj # type: ignore[assignment] self._gpr_index = ax - return obj, ax + return obj, ax, indexer + + @final + @property + def ax(self) -> Index: + warnings.warn( + f"{type(self).__name__}.ax is deprecated and will be removed in a " + "future version. Use Resampler.ax instead", + FutureWarning, + stacklevel=find_stack_level(), + ) + index = self._gpr_index + if index is None: + raise ValueError("_set_grouper must be called before ax is accessed") + return index + + @final + @property + def indexer(self): + warnings.warn( + f"{type(self).__name__}.indexer is deprecated and will be removed " + "in a future version. Use Resampler.indexer instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + return self._indexer_deprecated + + @final + @property + def obj(self): + warnings.warn( + f"{type(self).__name__}.obj is deprecated and will be removed " + "in a future version. Use GroupBy.indexer instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + return self._obj_deprecated @final @property diff --git a/pandas/core/resample.py b/pandas/core/resample.py index ccc32043b4dd2..8729c998db7d5 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -130,7 +130,7 @@ class Resampler(BaseGroupBy, PandasObject): _timegrouper: TimeGrouper binner: DatetimeIndex | TimedeltaIndex | PeriodIndex # depends on subclass exclusions: frozenset[Hashable] = frozenset() # for SelectionMixin compat - _internal_names_set = set({"obj", "ax"}) + _internal_names_set = set({"obj", "ax", "_indexer"}) # to the groupby descriptor _attributes = [ @@ -163,7 +163,7 @@ def __init__( self.group_keys = group_keys self.as_index = True - self.obj, self.ax = self._timegrouper._set_grouper( + self.obj, self.ax, self._indexer = self._timegrouper._set_grouper( self._convert_obj(obj), sort=True, gpr_index=gpr_index ) self.binner, self.grouper = self._get_binner() @@ -230,7 +230,7 @@ def _get_binner(self): """ binner, bins, binlabels = self._get_binner_for_time() assert len(bins) == len(binlabels) - bin_grouper = BinGrouper(bins, binlabels, indexer=self._timegrouper.indexer) + bin_grouper = BinGrouper(bins, binlabels, indexer=self._indexer) return binner, bin_grouper @Substitution( @@ -1674,7 +1674,7 @@ def _get_resampler(self, obj: NDFrame, kind=None) -> Resampler: TypeError if incompatible axis """ - _, ax = self._set_grouper(obj, gpr_index=None) + _, ax, indexer = self._set_grouper(obj, gpr_index=None) if isinstance(ax, DatetimeIndex): return DatetimeIndexResampler( diff --git a/pandas/tests/groupby/test_grouping.py b/pandas/tests/groupby/test_grouping.py index f7d4adc00260f..88ecac1ab24c9 100644 --- a/pandas/tests/groupby/test_grouping.py +++ b/pandas/tests/groupby/test_grouping.py @@ -1066,3 +1066,16 @@ def test_grouper_groups(): with tm.assert_produces_warning(FutureWarning, match=msg): res = grper.grouper assert res is gb.grouper + + msg = "Grouper.obj is deprecated and will be removed" + with tm.assert_produces_warning(FutureWarning, match=msg): + res = grper.obj + assert res is gb.obj + + msg = "Use Resampler.ax instead" + with tm.assert_produces_warning(FutureWarning, match=msg): + grper.ax + + msg = "Grouper.indexer is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + grper.indexer