Skip to content
Merged
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 @@ -793,6 +793,7 @@ MultiIndex
- Bug in :class:`DataFrame` arithmetic operations in case of unaligned MultiIndex columns (:issue:`60498`)
- Bug in :class:`DataFrame` arithmetic operations with :class:`Series` in case of unaligned MultiIndex (:issue:`61009`)
- Bug in :meth:`MultiIndex.from_tuples` causing wrong output with input of type tuples having NaN values (:issue:`60695`, :issue:`60988`)
- Bug in :meth:`DataFrame.reindex` and :meth:`Series.reindex` where reindexing :class:`Index` to a :class:`MultiIndex` would incorrectly set all values to ``NaN``.(:issue:`60923`)

I/O
^^^
Expand Down
12 changes: 12 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5373,6 +5373,18 @@ def reindex(
"""
# TODO: Decide if we care about having different examples for different
# kinds

# Automatically detect matching level when reindexing from Index to MultiIndex.
# This prevents values from being incorrectly set to NaN when the source index
# name matches a index name in the target MultiIndex
if (
level is None
and index is not None
and isinstance(index, MultiIndex)
and not isinstance(self.index, MultiIndex)
and self.index.name in index.names
):
level = self.index.name
self._check_copy_deprecation(copy)

if index is not None and columns is not None and labels is not None:
Expand Down
28 changes: 28 additions & 0 deletions pandas/tests/frame/methods/test_reindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -1258,3 +1258,31 @@ def test_invalid_method(self):
msg = "Invalid fill method"
with pytest.raises(ValueError, match=msg):
df.reindex([1, 0, 2], method="asfreq")

def test_reindex_index_name_matches_multiindex_level(self):
df = DataFrame(
{"value": [1, 2], "other": ["A", "B"]},
index=Index([10, 20], name="a"),
)
target = MultiIndex.from_product(
[[10, 20], ["x", "y"]],
names=["a", "b"],
)

result = df.reindex(index=target)
expected = DataFrame(
data={"value": [1, 1, 2, 2], "other": ["A", "A", "B", "B"]},
index=MultiIndex.from_product([[10, 20], ["x", "y"]], names=["a", "b"]),
)
tm.assert_frame_equal(result, expected)

def test_reindex_index_name_no_match_multiindex_level(self):
df = DataFrame({"value": [1, 2]}, index=Index([10, 20], name="different_name"))
target = MultiIndex.from_product([[10, 20], ["x", "y"]], names=["a", "b"])

result = df.reindex(index=target)
expected = DataFrame(
data={"value": [np.nan] * 4},
index=MultiIndex.from_product([[10, 20], ["x", "y"]], names=["a", "b"]),
)
tm.assert_frame_equal(result, expected)
38 changes: 38 additions & 0 deletions pandas/tests/series/methods/test_reindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,41 @@ def test_reindex_expand_nonnano_nat(dtype):
np.array([1, getattr(np, dtype)("nat", "s")], dtype=f"{dtype}[s]")
)
tm.assert_series_equal(result, expected)


@pytest.mark.parametrize(
"name, expected_match_level_a",
[
("a", True),
(None, False),
("x", False),
],
)
def test_reindex_multiindex_automatic_level(name, expected_match_level_a):
series = Series([26.73, 24.255], index=Index([81, 82], name=name))
target = MultiIndex.from_product(
[[81, 82], [np.nan], ["2018-06-01", "2018-07-01"]], names=["a", "b", "c"]
)

result = series.reindex(target)

if expected_match_level_a:
expected = Series(
data=[26.73, 26.73, 24.255, 24.255],
index=MultiIndex.from_product(
[[81, 82], [np.nan], ["2018-06-01", "2018-07-01"]],
names=["a", "b", "c"],
),
dtype=series.dtype,
)
else:
expected = Series(
data=[np.nan] * 4,
index=MultiIndex.from_product(
[[81, 82], [np.nan], ["2018-06-01", "2018-07-01"]],
names=["a", "b", "c"],
),
dtype=series.dtype,
)

tm.assert_series_equal(result, expected)
Loading