Skip to content

Commit 199cacb

Browse files
authored
BUG: Index/Series.to_frame not respecting explicit name=None (#44212)
1 parent 6312a8f commit 199cacb

File tree

7 files changed

+51
-8
lines changed

7 files changed

+51
-8
lines changed

doc/source/whatsnew/v1.4.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ Other
643643
- 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`)
644644
- Bug in :meth:`RangeIndex.union` with another ``RangeIndex`` with matching (even) ``step`` and starts differing by strictly less than ``step / 2`` (:issue:`44019`)
645645
- Bug in :meth:`RangeIndex.difference` with ``sort=None`` and ``step<0`` failing to sort (:issue:`44085`)
646+
- Bug in :meth:`Series.to_frame` and :meth:`Index.to_frame` ignoring the ``name`` argument when ``name=None`` is explicitly passed (:issue:`44212`)
646647

647648
.. ***DO NOT USE THIS SECTION***
648649

pandas/core/indexes/base.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,9 @@ def to_series(self, index=None, name: Hashable = None) -> Series:
15101510

15111511
return Series(self._values.copy(), index=index, name=name)
15121512

1513-
def to_frame(self, index: bool = True, name: Hashable = None) -> DataFrame:
1513+
def to_frame(
1514+
self, index: bool = True, name: Hashable = lib.no_default
1515+
) -> DataFrame:
15141516
"""
15151517
Create a DataFrame with a column containing the Index.
15161518
@@ -1561,7 +1563,7 @@ def to_frame(self, index: bool = True, name: Hashable = None) -> DataFrame:
15611563
"""
15621564
from pandas import DataFrame
15631565

1564-
if name is None:
1566+
if name is lib.no_default:
15651567
name = self.name or 0
15661568
result = DataFrame({name: self._values.copy()})
15671569

pandas/core/indexes/multi.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1684,7 +1684,7 @@ def unique(self, level=None):
16841684
level = self._get_level_number(level)
16851685
return self._get_level_values(level=level, unique=True)
16861686

1687-
def to_frame(self, index: bool = True, name=None) -> DataFrame:
1687+
def to_frame(self, index: bool = True, name=lib.no_default) -> DataFrame:
16881688
"""
16891689
Create a DataFrame with the levels of the MultiIndex as columns.
16901690
@@ -1736,7 +1736,7 @@ def to_frame(self, index: bool = True, name=None) -> DataFrame:
17361736
"""
17371737
from pandas import DataFrame
17381738

1739-
if name is not None:
1739+
if name is not lib.no_default:
17401740
if not is_list_like(name):
17411741
raise TypeError("'name' must be a list / sequence of column names.")
17421742

pandas/core/series.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,7 @@ def repeat(self, repeats, axis=None) -> Series:
13171317
)
13181318

13191319
@deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "level"])
1320-
def reset_index(self, level=None, drop=False, name=None, inplace=False):
1320+
def reset_index(self, level=None, drop=False, name=lib.no_default, inplace=False):
13211321
"""
13221322
Generate a new DataFrame or Series with the index reset.
13231323
@@ -1427,6 +1427,9 @@ def reset_index(self, level=None, drop=False, name=None, inplace=False):
14271427
"""
14281428
inplace = validate_bool_kwarg(inplace, "inplace")
14291429
if drop:
1430+
if name is lib.no_default:
1431+
name = self.name
1432+
14301433
new_index = default_index(len(self))
14311434
if level is not None:
14321435
if not isinstance(level, (tuple, list)):
@@ -1448,6 +1451,14 @@ def reset_index(self, level=None, drop=False, name=None, inplace=False):
14481451
"Cannot reset_index inplace on a Series to create a DataFrame"
14491452
)
14501453
else:
1454+
if name is lib.no_default:
1455+
# For backwards compatibility, keep columns as [0] instead of
1456+
# [None] when self.name is None
1457+
if self.name is None:
1458+
name = 0
1459+
else:
1460+
name = self.name
1461+
14511462
df = self.to_frame(name)
14521463
return df.reset_index(level=level, drop=drop)
14531464

@@ -1697,7 +1708,7 @@ def to_dict(self, into=dict):
16971708
into_c = com.standardize_mapping(into)
16981709
return into_c((k, maybe_box_native(v)) for k, v in self.items())
16991710

1700-
def to_frame(self, name=None) -> DataFrame:
1711+
def to_frame(self, name: Hashable = lib.no_default) -> DataFrame:
17011712
"""
17021713
Convert Series to DataFrame.
17031714
@@ -1723,7 +1734,7 @@ def to_frame(self, name=None) -> DataFrame:
17231734
2 c
17241735
"""
17251736
columns: Index
1726-
if name is None:
1737+
if name is lib.no_default:
17271738
name = self.name
17281739
if name is None:
17291740
# default to [0], same as we would get with DataFrame(self)

pandas/plotting/_matplotlib/core.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,11 @@ def _compute_plot_data(self):
460460
label = self.label
461461
if label is None and data.name is None:
462462
label = "None"
463-
data = data.to_frame(name=label)
463+
if label is None:
464+
# We'll end up with columns of [0] instead of [None]
465+
data = data.to_frame()
466+
else:
467+
data = data.to_frame(name=label)
464468
elif self._kind in ("hist", "box"):
465469
cols = self.columns if self.by is None else self.columns + self.by
466470
data = data.loc[:, cols]

pandas/tests/indexes/datetimes/methods/test_to_frame.py

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from pandas import (
22
DataFrame,
3+
Index,
34
date_range,
45
)
56
import pandas._testing as tm
@@ -12,3 +13,14 @@ def test_to_frame_datetime_tz(self):
1213
result = idx.to_frame()
1314
expected = DataFrame(idx, index=idx)
1415
tm.assert_frame_equal(result, expected)
16+
17+
def test_to_frame_respects_none_name(self):
18+
# GH#44212 if we explicitly pass name=None, then that should be respected,
19+
# not changed to 0
20+
idx = date_range(start="2019-01-01", end="2019-01-30", freq="D", tz="UTC")
21+
result = idx.to_frame(name=None)
22+
exp_idx = Index([None], dtype=object)
23+
tm.assert_index_equal(exp_idx, result.columns)
24+
25+
result = idx.rename("foo").to_frame(name=None)
26+
tm.assert_index_equal(exp_idx, result.columns)

pandas/tests/series/methods/test_to_frame.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
from pandas import (
22
DataFrame,
3+
Index,
34
Series,
45
)
56
import pandas._testing as tm
67

78

89
class TestToFrame:
10+
def test_to_frame_respects_name_none(self):
11+
# GH#44212 if we explicitly pass name=None, then that should be respected,
12+
# not changed to 0
13+
ser = Series(range(3))
14+
result = ser.to_frame(None)
15+
16+
exp_index = Index([None], dtype=object)
17+
tm.assert_index_equal(result.columns, exp_index)
18+
19+
result = ser.rename("foo").to_frame(None)
20+
tm.assert_index_equal(result.columns, exp_index)
21+
922
def test_to_frame(self, datetime_series):
1023
datetime_series.name = None
1124
rs = datetime_series.to_frame()

0 commit comments

Comments
 (0)