From 8064513319b7c33c5baa5f4ae0d008cfd6b8f27b Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 16 Mar 2023 08:38:45 -0700 Subject: [PATCH 1/2] DEPR: deprecate axis keyword in GroupBy aggregations --- doc/source/whatsnew/v2.1.0.rst | 1 + pandas/core/apply.py | 21 +++- pandas/core/groupby/generic.py | 36 ++++-- pandas/core/groupby/groupby.py | 118 ++++++++++++++++-- .../tests/groupby/aggregate/test_aggregate.py | 6 +- pandas/tests/groupby/test_function.py | 38 ++++-- pandas/tests/groupby/test_rank.py | 16 ++- 7 files changed, 201 insertions(+), 35 deletions(-) diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index e352a73a4e346..96fb64d72c007 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -102,6 +102,7 @@ Deprecations - Deprecated passing a :class:`DataFrame` to :meth:`DataFrame.from_records`, use :meth:`DataFrame.set_index` or :meth:`DataFrame.drop` instead (:issue:`51353`) - Deprecated accepting slices in :meth:`DataFrame.take`, call ``obj[slicer]`` or pass a sequence of integers instead (:issue:`51539`) - Deprecated 'method', 'limit', and 'fill_axis' keywords in :meth:`DataFrame.align` and :meth:`Series.align`, explicitly call ``fillna`` on the alignment results instead (:issue:`51856`) +- Deprecated the 'axis' keyword in :meth:`.GroupBy.idxmax`, :meth:`.GroupBy.idxmin`, :meth:`.GroupBy.fillna`, :meth:`.GroupBy.take`, :meth:`.GroupBy.skew`, :meth:`.GroupBy.rank`, :meth:`.GroupBy.cumprod`, :meth:`.GroupBy.cumsum`, :meth:`.GroupBy.cummax`, :meth:`.GroupBy.cummin`, :meth:`.GroupBy.pct_change`, :meth:`GroupBy.diff`, :meth:`.GroupBy.shift`, and :meth:`DataFrameGroupBy.corrwith`; for ``axis=1`` operate on the underlying :class:`DataFrame` instead (:issue:`50405`, :issue:`51046`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/apply.py b/pandas/core/apply.py index f23b05a5c41b8..f00b1416edeb2 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -480,6 +480,11 @@ def apply_str(self) -> DataFrame | Series: obj = self.obj + from pandas.core.groupby.generic import ( + DataFrameGroupBy, + SeriesGroupBy, + ) + # Support for `frame.transform('method')` # Some methods (shift, etc.) require the axis argument, others # don't, so inspect and insert if necessary. @@ -492,7 +497,21 @@ def apply_str(self) -> DataFrame | Series: ): raise ValueError(f"Operation {f} does not support axis=1") if "axis" in arg_names: - self.kwargs["axis"] = self.axis + if isinstance(obj, (SeriesGroupBy, DataFrameGroupBy)): + # Try to avoid FutureWarning for deprecated axis keyword; + # If self.axis matches the axis we would get by not passing + # axis, we safely exclude the keyword. + + default_axis = 0 + if f in ["idxmax", "idxmin"]: + # DataFrameGroupBy.idxmax, idxmin axis defaults to self.axis, + # whereas other axis keywords default to 0 + default_axis = self.obj.axis + + if default_axis != self.axis: + self.kwargs["axis"] = self.axis + else: + self.kwargs["axis"] = self.axis return self._try_aggregate_string_function(obj, f, *self.args, **self.kwargs) def apply_multiple(self) -> DataFrame | Series: diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index f922a6061e3c5..1754d92d22693 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -814,7 +814,7 @@ def fillna( self, value: object | ArrayLike | None = None, method: FillnaOptions | None = None, - axis: Axis | None = None, + axis: Axis | None = lib.no_default, inplace: bool = False, limit: int | None = None, downcast: dict | None = None, @@ -919,7 +919,7 @@ def fillna( def take( self, indices: TakeIndexer, - axis: Axis = 0, + axis: Axis | lib.NoDefault = lib.no_default, **kwargs, ) -> Series: """ @@ -1095,12 +1095,16 @@ def nsmallest( return result @doc(Series.idxmin.__doc__) - def idxmin(self, axis: Axis = 0, skipna: bool = True) -> Series: + def idxmin( + self, axis: Axis | lib.NoDefault = lib.no_default, skipna: bool = True + ) -> Series: result = self._op_via_apply("idxmin", axis=axis, skipna=skipna) return result.astype(self.obj.index.dtype) if result.empty else result @doc(Series.idxmax.__doc__) - def idxmax(self, axis: Axis = 0, skipna: bool = True) -> Series: + def idxmax( + self, axis: Axis | lib.NoDefault = lib.no_default, skipna: bool = True + ) -> Series: result = self._op_via_apply("idxmax", axis=axis, skipna=skipna) return result.astype(self.obj.index.dtype) if result.empty else result @@ -1922,7 +1926,7 @@ def nunique(self, dropna: bool = True) -> DataFrame: def idxmax( self, - axis: Axis | None = None, + axis: Axis | None | lib.NoDefault = lib.no_default, skipna: bool = True, numeric_only: bool = False, ) -> DataFrame: @@ -1994,7 +1998,12 @@ def idxmax( Beef co2_emissions dtype: object """ - if axis is None: + if axis is not lib.no_default: + if axis is None: + axis = self.axis + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "idxmax") + else: axis = self.axis def func(df): @@ -2008,7 +2017,7 @@ def func(df): def idxmin( self, - axis: Axis | None = None, + axis: Axis | None | lib.NoDefault = lib.no_default, skipna: bool = True, numeric_only: bool = False, ) -> DataFrame: @@ -2080,7 +2089,12 @@ def idxmin( Beef consumption dtype: object """ - if axis is None: + if axis is not lib.no_default: + if axis is None: + axis = self.axis + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "idxmin") + else: axis = self.axis def func(df): @@ -2211,7 +2225,7 @@ def fillna( self, value: Hashable | Mapping | Series | DataFrame = None, method: FillnaOptions | None = None, - axis: Axis | None = None, + axis: Axis | None | lib.NoDefault = lib.no_default, inplace: bool = False, limit=None, downcast=None, @@ -2345,7 +2359,7 @@ def fillna( def take( self, indices: TakeIndexer, - axis: Axis | None = 0, + axis: Axis | None | lib.NoDefault = lib.no_default, **kwargs, ) -> DataFrame: """ @@ -2598,7 +2612,7 @@ def dtypes(self) -> Series: def corrwith( self, other: DataFrame | Series, - axis: Axis = 0, + axis: Axis | lib.NoDefault = lib.no_default, drop: bool = False, method: CorrelationMethod = "pearson", numeric_only: bool = False, diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 96f39bb99e544..9d1f313668929 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -68,6 +68,7 @@ class providing the base-class of operations. cache_readonly, doc, ) +from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.cast import ensure_dtype_can_hold_na from pandas.core.dtypes.common import ( @@ -951,12 +952,45 @@ def __getattr__(self, attr: str): f"'{type(self).__name__}' object has no attribute '{attr}'" ) + @final + def _deprecate_axis(self, axis: int, name: str) -> None: + if axis == 1: + warnings.warn( + f"{type(self).__name__}.{name} with axis=1 is deprecated and " + "will be removed in a future version. Operate on the un-grouped " + "DataFrame instead", + FutureWarning, + stacklevel=find_stack_level(), + ) + else: + warnings.warn( + f"The 'axis' keyword in {type(self).__name__}.{name} is deprecated " + "and will be removed in a future version. " + "Call without passing 'axis' instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + @final def _op_via_apply(self, name: str, *args, **kwargs): """Compute the result of an operation by using GroupBy's apply.""" f = getattr(type(self._obj_with_exclusions), name) sig = inspect.signature(f) + if "axis" in kwargs and kwargs["axis"] is not lib.no_default: + axis = self.obj._get_axis_number(kwargs["axis"]) + self._deprecate_axis(axis, name) + elif "axis" in kwargs: + # exclude skew here because that was already defaulting to lib.no_default + # before this deprecation was instituted + if name == "skew": + pass + elif name == "fillna": + # maintain the behavior from before the deprecation + kwargs["axis"] = None + else: + kwargs["axis"] = 0 + # a little trickery for aggregation functions that need an axis # argument if "axis" in sig.parameters: @@ -3449,7 +3483,7 @@ def rank( ascending: bool = True, na_option: str = "keep", pct: bool = False, - axis: AxisInt = 0, + axis: AxisInt | lib.NoDefault = lib.no_default, ) -> NDFrameT: """ Provide the rank of values within each group. @@ -3516,6 +3550,12 @@ def rank( msg = "na_option must be one of 'keep', 'top', or 'bottom'" raise ValueError(msg) + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "rank") + else: + axis = 0 + kwargs = { "ties_method": method, "ascending": ascending, @@ -3541,7 +3581,9 @@ def rank( @final @Substitution(name="groupby") @Appender(_common_see_also) - def cumprod(self, axis: Axis = 0, *args, **kwargs) -> NDFrameT: + def cumprod( + self, axis: Axis | lib.NoDefault = lib.no_default, *args, **kwargs + ) -> NDFrameT: """ Cumulative product for each group. @@ -3550,6 +3592,12 @@ def cumprod(self, axis: Axis = 0, *args, **kwargs) -> NDFrameT: Series or DataFrame """ nv.validate_groupby_func("cumprod", args, kwargs, ["numeric_only", "skipna"]) + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "cumprod") + else: + axis = 0 + if axis != 0: f = lambda x: x.cumprod(axis=axis, **kwargs) return self._python_apply_general(f, self._selected_obj, is_transform=True) @@ -3559,7 +3607,9 @@ def cumprod(self, axis: Axis = 0, *args, **kwargs) -> NDFrameT: @final @Substitution(name="groupby") @Appender(_common_see_also) - def cumsum(self, axis: Axis = 0, *args, **kwargs) -> NDFrameT: + def cumsum( + self, axis: Axis | lib.NoDefault = lib.no_default, *args, **kwargs + ) -> NDFrameT: """ Cumulative sum for each group. @@ -3568,6 +3618,12 @@ def cumsum(self, axis: Axis = 0, *args, **kwargs) -> NDFrameT: Series or DataFrame """ nv.validate_groupby_func("cumsum", args, kwargs, ["numeric_only", "skipna"]) + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "cumsum") + else: + axis = 0 + if axis != 0: f = lambda x: x.cumsum(axis=axis, **kwargs) return self._python_apply_general(f, self._selected_obj, is_transform=True) @@ -3578,7 +3634,10 @@ def cumsum(self, axis: Axis = 0, *args, **kwargs) -> NDFrameT: @Substitution(name="groupby") @Appender(_common_see_also) def cummin( - self, axis: AxisInt = 0, numeric_only: bool = False, **kwargs + self, + axis: AxisInt | lib.NoDefault = lib.no_default, + numeric_only: bool = False, + **kwargs, ) -> NDFrameT: """ Cumulative min for each group. @@ -3588,6 +3647,12 @@ def cummin( Series or DataFrame """ skipna = kwargs.get("skipna", True) + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "cummin") + else: + axis = 0 + if axis != 0: f = lambda x: np.minimum.accumulate(x, axis) obj = self._selected_obj @@ -3603,7 +3668,10 @@ def cummin( @Substitution(name="groupby") @Appender(_common_see_also) def cummax( - self, axis: AxisInt = 0, numeric_only: bool = False, **kwargs + self, + axis: AxisInt | lib.NoDefault = lib.no_default, + numeric_only: bool = False, + **kwargs, ) -> NDFrameT: """ Cumulative max for each group. @@ -3613,6 +3681,12 @@ def cummax( Series or DataFrame """ skipna = kwargs.get("skipna", True) + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "cummax") + else: + axis = 0 + if axis != 0: f = lambda x: np.maximum.accumulate(x, axis) obj = self._selected_obj @@ -3763,7 +3837,13 @@ def blk_func(values: ArrayLike) -> ArrayLike: @final @Substitution(name="groupby") - def shift(self, periods: int = 1, freq=None, axis: Axis = 0, fill_value=None): + def shift( + self, + periods: int = 1, + freq=None, + axis: Axis | lib.NoDefault = lib.no_default, + fill_value=None, + ): """ Shift each group by periods observations. @@ -3789,6 +3869,12 @@ def shift(self, periods: int = 1, freq=None, axis: Axis = 0, fill_value=None): -------- Index.shift : Shift values of Index. """ + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "shift") + else: + axis = 0 + if freq is not None or axis != 0: f = lambda x: x.shift(periods, freq, axis, fill_value) return self._python_apply_general(f, self._selected_obj, is_transform=True) @@ -3810,7 +3896,9 @@ def shift(self, periods: int = 1, freq=None, axis: Axis = 0, fill_value=None): @final @Substitution(name="groupby") @Appender(_common_see_also) - def diff(self, periods: int = 1, axis: AxisInt = 0) -> NDFrameT: + def diff( + self, periods: int = 1, axis: AxisInt | lib.NoDefault = lib.no_default + ) -> NDFrameT: """ First discrete difference of element. @@ -3829,11 +3917,17 @@ def diff(self, periods: int = 1, axis: AxisInt = 0) -> NDFrameT: Series or DataFrame First differences. """ + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "diff") + else: + axis = 0 + if axis != 0: return self.apply(lambda x: x.diff(periods=periods, axis=axis)) obj = self._obj_with_exclusions - shifted = self.shift(periods=periods, axis=axis) + shifted = self.shift(periods=periods) # GH45562 - to retain existing behavior and match behavior of Series.diff(), # int8 and int16 are coerced to float32 rather than float64. @@ -3857,7 +3951,7 @@ def pct_change( fill_method: FillnaOptions = "ffill", limit=None, freq=None, - axis: Axis = 0, + axis: Axis | lib.NoDefault = lib.no_default, ): """ Calculate pct_change of each value to previous entry in group. @@ -3867,6 +3961,12 @@ def pct_change( Series or DataFrame Percentage changes within each group. """ + if axis is not lib.no_default: + axis = self.obj._get_axis_number(axis) + self._deprecate_axis(axis, "pct_change") + else: + axis = 0 + # TODO(GH#23918): Remove this conditional for SeriesGroupBy when # GH#23918 is fixed if freq is not None or axis != 0: diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index ad53cf6629adb..6955fa6b63732 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -228,14 +228,18 @@ def test_std_masked_dtype(any_numeric_ea_dtype): def test_agg_str_with_kwarg_axis_1_raises(df, reduction_func): gb = df.groupby(level=0) + warn_msg = f"DataFrameGroupBy.{reduction_func} with axis=1 is deprecated" if reduction_func in ("idxmax", "idxmin"): error = TypeError msg = "reduction operation '.*' not allowed for this dtype" + warn = FutureWarning else: error = ValueError msg = f"Operation {reduction_func} does not support axis=1" + warn = None with pytest.raises(error, match=msg): - gb.agg(reduction_func, axis=1) + with tm.assert_produces_warning(warn, match=warn_msg): + gb.agg(reduction_func, axis=1) @pytest.mark.parametrize( diff --git a/pandas/tests/groupby/test_function.py b/pandas/tests/groupby/test_function.py index e6709329f8f52..9ca2695e0d091 100644 --- a/pandas/tests/groupby/test_function.py +++ b/pandas/tests/groupby/test_function.py @@ -343,10 +343,15 @@ def test_cython_api2(): tm.assert_frame_equal(result, expected) # GH 13994 - result = df.groupby("A").cumsum(axis=1) + msg = "DataFrameGroupBy.cumsum with axis=1 is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = df.groupby("A").cumsum(axis=1) expected = df.cumsum(axis=1) tm.assert_frame_equal(result, expected) - result = df.groupby("A").cumprod(axis=1) + + msg = "DataFrameGroupBy.cumprod with axis=1 is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = df.groupby("A").cumprod(axis=1) expected = df.cumprod(axis=1) tm.assert_frame_equal(result, expected) @@ -499,7 +504,9 @@ def test_idxmin_idxmax_axis1(): gb = df.groupby("A") - res = gb.idxmax(axis=1) + warn_msg = "DataFrameGroupBy.idxmax with axis=1 is deprecated" + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + res = gb.idxmax(axis=1) alt = df.iloc[:, 1:].idxmax(axis=1) indexer = res.index.get_level_values(1) @@ -511,7 +518,8 @@ def test_idxmin_idxmax_axis1(): msg = "reduction operation 'argmax' not allowed for this dtype" with pytest.raises(TypeError, match=msg): - gb2.idxmax(axis=1) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + gb2.idxmax(axis=1) @pytest.mark.parametrize("numeric_only", [True, False, None]) @@ -549,10 +557,16 @@ def test_axis1_numeric_only(request, groupby_func, numeric_only): "idxmax", "fillna", ) + warn_msg = f"DataFrameGroupBy.{groupby_func} with axis=1 is deprecated" if numeric_only is not None and groupby_func in no_args: msg = "got an unexpected keyword argument 'numeric_only'" - with pytest.raises(TypeError, match=msg): - method(*args, **kwargs) + if groupby_func in ["cumprod", "cumsum"]: + with pytest.raises(TypeError, match=msg): + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + method(*args, **kwargs) + else: + with pytest.raises(TypeError, match=msg): + method(*args, **kwargs) elif groupby_func not in has_axis: msg = "got an unexpected keyword argument 'axis'" with pytest.raises(TypeError, match=msg): @@ -571,9 +585,11 @@ def test_axis1_numeric_only(request, groupby_func, numeric_only): "unsupported operand type", ) with pytest.raises(TypeError, match=f"({'|'.join(msgs)})"): - method(*args, **kwargs) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + method(*args, **kwargs) else: - result = method(*args, **kwargs) + with tm.assert_produces_warning(FutureWarning, match=warn_msg): + result = method(*args, **kwargs) df_expected = df.drop(columns="E").T if numeric_only else df.T expected = getattr(df_expected, groupby_func)(*args).T @@ -1599,7 +1615,11 @@ def test_groupby_empty_dataset(dtype, kwargs): def test_corrwith_with_1_axis(): # GH 47723 df = DataFrame({"a": [1, 1, 2], "b": [3, 7, 4]}) - result = df.groupby("a").corrwith(df, axis=1) + gb = df.groupby("a") + + msg = "DataFrameGroupBy.corrwith with axis=1 is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = gb.corrwith(df, axis=1) index = Index( data=[(1, 0), (1, 1), (1, 2), (2, 2), (2, 0), (2, 1)], name=("a", None), diff --git a/pandas/tests/groupby/test_rank.py b/pandas/tests/groupby/test_rank.py index 9f42f6ad72591..cde661238da67 100644 --- a/pandas/tests/groupby/test_rank.py +++ b/pandas/tests/groupby/test_rank.py @@ -622,7 +622,9 @@ def test_rank_multiindex(): msg = "DataFrame.groupby with axis=1 is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): gb = df.groupby(level=0, axis=1) - result = gb.rank(axis=1) + msg = "DataFrameGroupBy.rank with axis=1 is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = gb.rank(axis=1) expected = concat( [ @@ -645,7 +647,9 @@ def test_groupby_axis0_rank_axis1(): with tm.assert_produces_warning(FutureWarning, match=msg): gb = df.groupby(level=0, axis=0) - res = gb.rank(axis=1) + msg = "DataFrameGroupBy.rank with axis=1 is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + res = gb.rank(axis=1) # This should match what we get when "manually" operating group-by-group expected = concat([df.loc["a"].rank(axis=1), df.loc["b"].rank(axis=1)], axis=0) @@ -653,7 +657,9 @@ def test_groupby_axis0_rank_axis1(): # check that we haven't accidentally written a case that coincidentally # matches rank(axis=0) - alt = gb.rank(axis=0) + msg = "The 'axis' keyword in DataFrameGroupBy.rank" + with tm.assert_produces_warning(FutureWarning, match=msg): + alt = gb.rank(axis=0) assert not alt.equals(expected) @@ -669,7 +675,9 @@ def test_groupby_axis0_cummax_axis1(): with tm.assert_produces_warning(FutureWarning, match=msg): gb = df.groupby(level=0, axis=0) - cmax = gb.cummax(axis=1) + msg = "DataFrameGroupBy.cummax with axis=1 is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + cmax = gb.cummax(axis=1) expected = df[[0, 1]].astype(np.float64) expected[2] = expected[1] tm.assert_frame_equal(cmax, expected) From 8b2db1b1669d8e90dca64b1059465abab709e9a1 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 16 Mar 2023 09:52:11 -0700 Subject: [PATCH 2/2] mypy fixup, deprecations in docstrings --- pandas/core/groupby/generic.py | 38 +++++++++++++++++++++++++++++++--- pandas/core/groupby/groupby.py | 13 ++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 1754d92d22693..ce4cd3476ec83 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -814,7 +814,7 @@ def fillna( self, value: object | ArrayLike | None = None, method: FillnaOptions | None = None, - axis: Axis | None = lib.no_default, + axis: Axis | None | lib.NoDefault = lib.no_default, inplace: bool = False, limit: int | None = None, downcast: dict | None = None, @@ -838,6 +838,11 @@ def fillna( ``'bfill'`` will use next valid observation to fill the gap. axis : {0 or 'index', 1 or 'columns'} Unused, only for compatibility with :meth:`DataFrameGroupBy.fillna`. + + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + inplace : bool, default False Broken. Do not set to True. limit : int, default None @@ -941,6 +946,11 @@ def take( The axis on which to select elements. ``0`` means that we are selecting rows, ``1`` means that we are selecting columns. For `SeriesGroupBy` this parameter is unused and defaults to 0. + + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + **kwargs For compatibility with :meth:`numpy.take`. Has no effect on the output. @@ -1016,6 +1026,10 @@ def skew( Axis for the function to be applied on. This parameter is only for compatibility with DataFrame and is unused. + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + skipna : bool, default True Exclude NA/null values when computing the result. @@ -1943,6 +1957,10 @@ def idxmax( .. versionchanged:: 2.0.0 + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + skipna : bool, default True Exclude NA/null values. If an entire row/column is NA, the result will be NA. @@ -2034,6 +2052,10 @@ def idxmin( .. versionchanged:: 2.0.0 + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + skipna : bool, default True Exclude NA/null values. If an entire row/column is NA, the result will be NA. @@ -2254,8 +2276,9 @@ def fillna( :class:`DataFrameGroupBy` ``axis`` argument is ``1``, using ``axis=0`` or ``axis=1`` here will produce the same results. - .. deprecated:: 2.0.0 - Use frame.T.groupby(...) instead. + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. inplace : bool, default False Broken. Do not set to True. @@ -2380,6 +2403,11 @@ def take( axis : {0 or 'index', 1 or 'columns', None}, default 0 The axis on which to select elements. ``0`` means that we are selecting rows, ``1`` means that we are selecting columns. + + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + **kwargs For compatibility with :meth:`numpy.take`. Has no effect on the output. @@ -2473,6 +2501,10 @@ def skew( .. versionadded:: 2.0.0 + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + skipna : bool, default True Exclude NA/null values when computing the result. diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 9d1f313668929..50c747767cc7d 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -3507,6 +3507,10 @@ def rank( axis : int, default 0 The axis of the object over which to compute the rank. + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + Returns ------- DataFrame with ranking of values within each group @@ -3857,6 +3861,11 @@ def shift( Frequency string. axis : axis to shift, default 0 Shift direction. + + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + fill_value : optional The scalar value to use for newly introduced missing values. @@ -3912,6 +3921,10 @@ def diff( axis : axis to shift, default 0 Take difference over rows (0) or columns (1). + .. deprecated:: 2.1.0 + For axis=1, operate on the underlying object instead. Otherwise + the axis keyword is not necessary. + Returns ------- Series or DataFrame