Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.1.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Bug fixes
- Bug in :func:`read_spss` where passing a ``pathlib.Path`` as ``path`` would raise a ``TypeError`` (:issue:`33666`)
- Bug in :meth:`Series.str.startswith` and :meth:`Series.str.endswith` with ``category`` dtype not propagating ``na`` parameter (:issue:`36241`)
- Bug in :class:`Series` constructor where integer overflow would occur for sufficiently large scalar inputs when an index was provided (:issue:`36291`)
- Bug in :meth:`DataFrame.stack` raising a ``ValueError`` when stacking :class:`MultiIndex` columns based on position when the levels had duplicate names (:issue:`36353`)

.. ---------------------------------------------------------------------------

Expand Down
14 changes: 4 additions & 10 deletions pandas/core/reshape/reshape.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,19 +586,13 @@ def _stack_multi_columns(frame, level_num=-1, dropna=True):
def _convert_level_number(level_num, columns):
"""
Logic for converting the level number to something we can safely pass
to swaplevel:

We generally want to convert the level number into a level name, except
when columns do not have names, in which case we must leave as a level
number
to swaplevel. If `level_num` matches a column name return the name from
position `level_num`, otherwise return `level_num`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the first line of a docstring should be a one -liner. see https://numpydoc.readthedocs.io/en/latest/format.html#sections

(for internal functions, imo we could follow less strict guidelines, see #32773)

"""
if level_num in columns.names:
return columns.names[level_num]
else:
if columns.names[level_num] is None:
return level_num
else:
return columns.names[level_num]

return level_num

this = frame.copy()

Expand Down
13 changes: 13 additions & 0 deletions pandas/tests/frame/test_reshape.py
Original file line number Diff line number Diff line change
Expand Up @@ -1302,3 +1302,16 @@ def test_unstacking_multi_index_df():
),
)
tm.assert_frame_equal(result, expected)


def test_stack_positional_level_duplicate_column_names():
# https://github.com/pandas-dev/pandas/issues/36353
columns = pd.MultiIndex.from_product([("x", "y"), ("y", "z")], names=["a", "a"])
df = pd.DataFrame([[1, 1, 1, 1]], columns=columns)
result = df.stack(0)

new_columns = pd.Index(["y", "z"], name="a")
new_index = pd.MultiIndex.from_tuples([(0, "x"), (0, "y")], names=[None, "a"])
expected = pd.DataFrame([[1, 1], [1, 1]], index=new_index, columns=new_columns)

tm.assert_frame_equal(result, expected)