From 1231bb159101b9501437e9762250b928857aa3b4 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 22 Mar 2023 18:12:49 -0700 Subject: [PATCH 1/2] DEPR: Categorical.to_list, NDFrame.align broadcast_axis --- doc/source/whatsnew/v2.1.0.rst | 2 ++ pandas/core/arrays/categorical.py | 9 ++++++ pandas/core/frame.py | 2 +- pandas/core/generic.py | 33 +++++++++++++++++++-- pandas/core/series.py | 2 +- pandas/tests/arrays/categorical/test_api.py | 7 +++++ pandas/tests/frame/methods/test_align.py | 4 ++- 7 files changed, 54 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 706dcaa34366a..ba6e0bcce0bb4 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -104,11 +104,13 @@ Deprecations - Deprecated the default of ``observed=False`` in :meth:`DataFrame.groupby` and :meth:`Series.groupby`; this will default to ``True`` in a future version (:issue:`43999`) - Deprecated :meth:`DataFrameGroupBy.dtypes`, check ``dtypes`` on the underlying object instead (:issue:`51045`) - Deprecated ``axis=1`` in :meth:`DataFrame.groupby` and in :class:`Grouper` constructor, do ``frame.T.groupby(...)`` instead (:issue:`51203`) +- Deprecated :meth:`Categorical.to_list`, use ``obj.tolist()`` instead (:issue:`51254`) - 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 ``axis=1`` in :meth:`DataFrame.ewm`, :meth:`DataFrame.rolling`, :meth:`DataFrame.expanding`, transpose before calling the method instead (:issue:`51778`) - Deprecated the ``axis`` keyword in :meth:`DataFrame.ewm`, :meth:`Series.ewm`, :meth:`DataFrame.rolling`, :meth:`Series.rolling`, :meth:`DataFrame.expanding`, :meth:`Series.expanding` (:issue:`51778`) - 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 'broadcast_axis' keyword in :meth:`Series.align` and :meth:`DataFrame.align`, upcast before calling ``align`` with ``left = DataFrame({col: left for col in right.columns}, index=right.index)`` (: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/arrays/categorical.py b/pandas/core/arrays/categorical.py index 9de83933690f4..0a79004871f5f 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -13,6 +13,7 @@ cast, overload, ) +import warnings import numpy as np @@ -25,6 +26,7 @@ ) from pandas._libs.arrays import NDArrayBacked from pandas.compat.numpy import function as nv +from pandas.util._exceptions import find_stack_level from pandas.util._validators import validate_bool_kwarg from pandas.core.dtypes.cast import ( @@ -551,6 +553,13 @@ def to_list(self): """ Alias for tolist. """ + # GH#51254 + warnings.warn( + "Categorical.to_list is deprecated and will be removed in a future " + "version. Use obj.tolist() instead", + FutureWarning, + stacklevel=find_stack_level(), + ) return self.tolist() @classmethod diff --git a/pandas/core/frame.py b/pandas/core/frame.py index a2e3b6fc10e43..0aadef410b8f8 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -5033,7 +5033,7 @@ def align( method: FillnaOptions | None | lib.NoDefault = lib.no_default, limit: int | None | lib.NoDefault = lib.no_default, fill_axis: Axis | lib.NoDefault = lib.no_default, - broadcast_axis: Axis | None = None, + broadcast_axis: Axis | None | lib.NoDefault = lib.no_default, ) -> tuple[Self, NDFrameT]: return super().align( other, diff --git a/pandas/core/generic.py b/pandas/core/generic.py index d621a1c68b0f8..f81da33aa7dd7 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9333,7 +9333,7 @@ def align( method: FillnaOptions | None | lib.NoDefault = lib.no_default, limit: int | None | lib.NoDefault = lib.no_default, fill_axis: Axis | lib.NoDefault = lib.no_default, - broadcast_axis: Axis | None = None, + broadcast_axis: Axis | None | lib.NoDefault = lib.NoDefault, ) -> tuple[Self, NDFrameT]: """ Align two objects on their axes with the specified join method. @@ -9382,6 +9382,8 @@ def align( Broadcast values along this axis, if aligning two objects of different dimensions. + .. deprecated:: 2.1 + Returns ------- tuple of ({klass}, type of other) @@ -9474,6 +9476,27 @@ def align( limit = None method = clean_fill_method(method) + if broadcast_axis is not lib.no_default: + # GH#51856 + msg = ( + f"The 'broadcast_axis' keyword in {type(self).__name__}.align is " + "deprecated and will be removed in a future version." + ) + if broadcast_axis is not None: + if self.ndim == 1 and other.ndim == 2: + msg += ( + " Use left = DataFrame({col: left for col in right.columns}, " + "index=right.index) before calling `left.align(right)` instead." + ) + elif self.ndim == 2 and other.ndim == 1: + msg += ( + " Use right = DataFrame({col: right for col in left.columns}, " + "index=left.index) before calling `left.align(right)` instead" + ) + warnings.warn(msg, FutureWarning, stacklevel=find_stack_level()) + else: + broadcast_axis = None + if broadcast_axis == 1 and self.ndim != other.ndim: if isinstance(self, ABCSeries): # this means other is a DataFrame, and we need to broadcast @@ -9719,7 +9742,13 @@ def _where( cond = common.apply_if_callable(cond, self) if isinstance(cond, NDFrame): # CoW: Make sure reference is not kept alive - cond = cond.align(self, join="right", broadcast_axis=1, copy=False)[0] + if cond.ndim == 1 and self.ndim == 2: + cond = cond._constructor_expanddim( + {i: cond for i in range(len(self.columns))}, + copy=False, + ) + cond.columns = self.columns + cond = cond.align(self, join="right", copy=False)[0] else: if not hasattr(cond, "shape"): cond = np.asanyarray(cond) diff --git a/pandas/core/series.py b/pandas/core/series.py index f8875555fdf97..95e63e346eaea 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -4580,7 +4580,7 @@ def align( method: FillnaOptions | None | lib.NoDefault = lib.no_default, limit: int | None | lib.NoDefault = lib.no_default, fill_axis: Axis | lib.NoDefault = lib.no_default, - broadcast_axis: Axis | None = None, + broadcast_axis: Axis | None | lib.NoDefault = lib.no_default, ) -> tuple[Self, NDFrameT]: return super().align( other, diff --git a/pandas/tests/arrays/categorical/test_api.py b/pandas/tests/arrays/categorical/test_api.py index aa02585ee81a5..7d96890b6c669 100644 --- a/pandas/tests/arrays/categorical/test_api.py +++ b/pandas/tests/arrays/categorical/test_api.py @@ -18,6 +18,13 @@ class TestCategoricalAPI: + def test_to_list_deprecated(self): + # GH#51254 + cat1 = Categorical(list("acb"), ordered=False) + msg = "Categorical.to_list is deprecated and will be removed" + with tm.assert_produces_warning(FutureWarning, match=msg): + cat1.to_list() + def test_ordered_api(self): # GH 9347 cat1 = Categorical(list("acb"), ordered=False) diff --git a/pandas/tests/frame/methods/test_align.py b/pandas/tests/frame/methods/test_align.py index 995bbec11a5c4..f968787eb5c4d 100644 --- a/pandas/tests/frame/methods/test_align.py +++ b/pandas/tests/frame/methods/test_align.py @@ -126,7 +126,9 @@ def test_align_frame_with_series(self, float_frame): tm.assert_index_equal(right.index, float_frame.index) assert isinstance(right, Series) - left, right = float_frame.align(s, broadcast_axis=1) + msg = "The 'broadcast_axis' keyword in DataFrame.align is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + left, right = float_frame.align(s, broadcast_axis=1) tm.assert_index_equal(left.index, float_frame.index) expected = {c: s for c in float_frame.columns} expected = DataFrame( From 4a66b80c2795fbfdfc57ca7985e6bc3479ffe322 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 22 Mar 2023 18:52:00 -0700 Subject: [PATCH 2/2] typo fixup --- pandas/core/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index f81da33aa7dd7..d5a316c7336da 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9333,7 +9333,7 @@ def align( method: FillnaOptions | None | lib.NoDefault = lib.no_default, limit: int | None | lib.NoDefault = lib.no_default, fill_axis: Axis | lib.NoDefault = lib.no_default, - broadcast_axis: Axis | None | lib.NoDefault = lib.NoDefault, + broadcast_axis: Axis | None | lib.NoDefault = lib.no_default, ) -> tuple[Self, NDFrameT]: """ Align two objects on their axes with the specified join method.