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 @@ -787,6 +787,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:`Series.reindex` where reindexing a Series with a named Index to a MultiIndex would incorrectly set all values to ``NaN``. Now correctly preserves values when the source index name matches a target level name (:issue:`60923`)

I/O
^^^
Expand Down
9 changes: 9 additions & 0 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -4886,6 +4886,15 @@ def reindex( # type: ignore[override]
limit: int | None = None,
tolerance=None,
) -> Series:
# 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 level name in the target MultiIndex
if (
level is None
and isinstance(index, MultiIndex)
and self.index.name in index.names
):
level = self.index.name
return super().reindex(
index=index,
method=method,
Expand Down
46 changes: 46 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,49 @@ 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(
"idx,expected_match_level_a",
[
# Source index has matching name - should match level "a"
(Index([81, 82], name="a"), True),
# Source index has no name - should not match any level
(Index([81, 82]), False),
# Source index name doesn't match any level - should not match
(Index([81, 82], name="x"), False),
],
)
def test_reindex_multiindex_automatic_level(idx, expected_match_level_a):
"""
Test automatic level detection when reindexing from Index to MultiIndex.
"""
series = Series([26.73, 24.255], index=idx)
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:
# Should match behavior of explicit level="a"
expected = series.reindex(target, level="a")
else:
# Should contain all NaN values
expected = Series(np.nan, index=target, dtype=series.dtype)

tm.assert_series_equal(result, expected)


def test_reindex_multiindex_explicit_level_overrides():
"""
Test that explicit level parameter overrides automatic detection.
"""
series = Series([26.73, 24.255], index=Index([81, 82], name="a"))
target = MultiIndex.from_product(
[[81, 82], [np.nan], ["2018-06-01", "2018-07-01"]], names=["a", "b", "c"]
)

result = series.reindex(target, level=0)
expected = series.reindex(target, level="a")
tm.assert_series_equal(result, expected)
Loading