Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enhancement2

Other enhancements
^^^^^^^^^^^^^^^^^^
- :attr:`MultiIndex.names` containing ``None`` no longer throws ``AssertionError`` during join (:issue:`58721`)
Copy link
Member

Choose a reason for hiding this comment

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

Can you move the note to the Reshaping section of bugfixes.

- :class:`pandas.api.typing.FrozenList` is available for typing the outputs of :attr:`MultiIndex.names`, :attr:`MultiIndex.codes` and :attr:`MultiIndex.levels` (:issue:`58237`)
- :class:`pandas.api.typing.SASReader` is available for typing the output of :func:`read_sas` (:issue:`55689`)
- :func:`DataFrame.to_excel` now raises an ``UserWarning`` when the character count in a cell exceeds Excel's limitation of 32767 characters (:issue:`56954`)
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4516,8 +4516,8 @@ def _join_multi(self, other: Index, how: JoinHow):
from pandas.core.reshape.merge import restore_dropped_levels_multijoin

# figure out join names
self_names_list = list(com.not_none(*self.names))
other_names_list = list(com.not_none(*other.names))
self_names_list = list(self.names)
other_names_list = list(other.names)
self_names_order = self_names_list.index
other_names_order = other_names_list.index
self_names = set(self_names_list)
Expand Down
26 changes: 26 additions & 0 deletions pandas/tests/reshape/merge/test_join.py
Original file line number Diff line number Diff line change
Expand Up @@ -1098,3 +1098,29 @@ def test_join_multiindex_categorical_output_index_dtype(how, values):

result = df1.join(df2, how=how)
tm.assert_frame_equal(result, expected)


def test_join_multiindex_with_none_as_label():
# GH 58721
df1 = DataFrame(
{"A": [1]},
index=MultiIndex.from_tuples([(3, 3)], names=["X", None]),
)
df2 = DataFrame(
{"B": [2]},
index=MultiIndex.from_tuples([(3, 3)], names=[None, "X"]),
)

result12 = df1.join(df2)
expected12 = DataFrame(
{"A": [1], "B": [2]},
index=MultiIndex.from_tuples([(3, 3)], names=["X", None]),
)
tm.assert_frame_equal(result12, expected12)

result21 = df2.join(df1)
expected21 = DataFrame(
{"B": [2], "A": [1]},
index=MultiIndex.from_tuples([(3, 3)], names=[None, "X"]),
)
tm.assert_frame_equal(result21, expected21)
Loading