From 0d44b72c5ff9a5db7cabfa585e9ce2ab91a3bb71 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 21 Jan 2022 12:51:14 +0100 Subject: [PATCH] DEPR: restore to_frame() name=None behaviour but deprecate it --- doc/source/whatsnew/v1.4.0.rst | 2 +- pandas/core/indexes/base.py | 11 +++++++++++ pandas/core/indexes/multi.py | 11 +++++++++++ pandas/core/series.py | 13 ++++++++++++- .../indexes/datetimes/methods/test_to_frame.py | 11 ++++++++--- pandas/tests/series/methods/test_to_frame.py | 11 ++++++++--- 6 files changed, 51 insertions(+), 8 deletions(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 1ca4e8cc97df0..234fada5e2ba3 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -696,6 +696,7 @@ Other Deprecations - Deprecated the behavior of :meth:`Timestamp.utcfromtimestamp`, in the future it will return a timezone-aware UTC :class:`Timestamp` (:issue:`22451`) - Deprecated :meth:`NaT.freq` (:issue:`45071`) - Deprecated behavior of :class:`Series` and :class:`DataFrame` construction when passed float-dtype data containing ``NaN`` and an integer dtype ignoring the dtype argument; in a future version this will raise (:issue:`40110`) +- Deprecated the behaviour of :meth:`Series.to_frame` and :meth:`Index.to_frame` to ignore the ``name`` argument when ``name=None``. Currently, this means to preserve the existing name, but in the future explicitly passing ``name=None`` will set ``None`` as the name of the column in the resulting DataFrame (:issue:`44212`) .. --------------------------------------------------------------------------- @@ -1041,7 +1042,6 @@ Other - Bug in :meth:`CustomBusinessMonthBegin.__add__` (:meth:`CustomBusinessMonthEnd.__add__`) not applying the extra ``offset`` parameter when beginning (end) of the target month is already a business day (:issue:`41356`) - Bug in :meth:`RangeIndex.union` with another ``RangeIndex`` with matching (even) ``step`` and starts differing by strictly less than ``step / 2`` (:issue:`44019`) - Bug in :meth:`RangeIndex.difference` with ``sort=None`` and ``step<0`` failing to sort (:issue:`44085`) -- Bug in :meth:`Series.to_frame` and :meth:`Index.to_frame` ignoring the ``name`` argument when ``name=None`` is explicitly passed (:issue:`44212`) - Bug in :meth:`Series.replace` and :meth:`DataFrame.replace` with ``value=None`` and ExtensionDtypes (:issue:`44270`, :issue:`37899`) - Bug in :meth:`FloatingArray.equals` failing to consider two arrays equal if they contain ``np.nan`` values (:issue:`44382`) - Bug in :meth:`DataFrame.shift` with ``axis=1`` and ``ExtensionDtype`` columns incorrectly raising when an incompatible ``fill_value`` is passed (:issue:`44564`) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 717abb998dd65..16b44ca4da162 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1628,6 +1628,17 @@ def to_frame( """ from pandas import DataFrame + if name is None: + warnings.warn( + "Explicitly passing `name=None` currently preserves the Index's name " + "or uses a default name of 0. This behaviour is deprecated, and in " + "the future `None` will be used as the name of the resulting " + "DataFrame column.", + FutureWarning, + stacklevel=find_stack_level(), + ) + name = lib.no_default + if name is lib.no_default: name = self.name or 0 result = DataFrame({name: self._values.copy()}) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 676fa4962cd56..7e59e51174b6f 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1762,6 +1762,17 @@ def to_frame(self, index: bool = True, name=lib.no_default) -> DataFrame: """ from pandas import DataFrame + if name is None: + warnings.warn( + "Explicitly passing `name=None` currently preserves the Index's name " + "or uses a default name of 0. This behaviour is deprecated, and in " + "the future `None` will be used as the name of the resulting " + "DataFrame column.", + FutureWarning, + stacklevel=find_stack_level(), + ) + name = lib.no_default + if name is not lib.no_default: if not is_list_like(name): raise TypeError("'name' must be a list / sequence of column names.") diff --git a/pandas/core/series.py b/pandas/core/series.py index 4e69596539bed..cc60cd63ba3ab 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1758,7 +1758,7 @@ def to_frame(self, name: Hashable = lib.no_default) -> DataFrame: Parameters ---------- - name : object, default None + name : object, optional The passed name should substitute for the series name (if it has one). @@ -1777,6 +1777,17 @@ def to_frame(self, name: Hashable = lib.no_default) -> DataFrame: 1 b 2 c """ + if name is None: + warnings.warn( + "Explicitly passing `name=None` currently preserves the Series' name " + "or uses a default name of 0. This behaviour is deprecated, and in " + "the future `None` will be used as the name of the resulting " + "DataFrame column.", + FutureWarning, + stacklevel=find_stack_level(), + ) + name = lib.no_default + columns: Index if name is lib.no_default: name = self.name diff --git a/pandas/tests/indexes/datetimes/methods/test_to_frame.py b/pandas/tests/indexes/datetimes/methods/test_to_frame.py index 80e8284abe031..fa5cca1c3e78b 100644 --- a/pandas/tests/indexes/datetimes/methods/test_to_frame.py +++ b/pandas/tests/indexes/datetimes/methods/test_to_frame.py @@ -17,10 +17,15 @@ def test_to_frame_datetime_tz(self): def test_to_frame_respects_none_name(self): # GH#44212 if we explicitly pass name=None, then that should be respected, # not changed to 0 + # GH-45448 this is first deprecated to only change in the future idx = date_range(start="2019-01-01", end="2019-01-30", freq="D", tz="UTC") - result = idx.to_frame(name=None) - exp_idx = Index([None], dtype=object) + with tm.assert_produces_warning(FutureWarning): + result = idx.to_frame(name=None) + # exp_idx = Index([None], dtype=object) + exp_idx = Index([0]) tm.assert_index_equal(exp_idx, result.columns) - result = idx.rename("foo").to_frame(name=None) + with tm.assert_produces_warning(FutureWarning): + result = idx.rename("foo").to_frame(name=None) + exp_idx = Index(["foo"], dtype=object) tm.assert_index_equal(exp_idx, result.columns) diff --git a/pandas/tests/series/methods/test_to_frame.py b/pandas/tests/series/methods/test_to_frame.py index 55d49b8fbee70..5f303d09dcc33 100644 --- a/pandas/tests/series/methods/test_to_frame.py +++ b/pandas/tests/series/methods/test_to_frame.py @@ -10,13 +10,18 @@ class TestToFrame: def test_to_frame_respects_name_none(self): # GH#44212 if we explicitly pass name=None, then that should be respected, # not changed to 0 + # GH-45448 this is first deprecated to only change in the future ser = Series(range(3)) - result = ser.to_frame(None) + with tm.assert_produces_warning(FutureWarning): + result = ser.to_frame(None) - exp_index = Index([None], dtype=object) + # exp_index = Index([None], dtype=object) + exp_index = Index([0]) tm.assert_index_equal(result.columns, exp_index) - result = ser.rename("foo").to_frame(None) + with tm.assert_produces_warning(FutureWarning): + result = ser.rename("foo").to_frame(None) + exp_index = Index(["foo"], dtype=object) tm.assert_index_equal(result.columns, exp_index) def test_to_frame(self, datetime_series):