From c08ee422b927036815cf97ad104f1a34591f4559 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 29 Oct 2025 10:54:54 -0700 Subject: [PATCH] BUG: rename with Series with non-unique index --- doc/source/whatsnew/v3.0.0.rst | 1 + pandas/core/generic.py | 4 +++ pandas/tests/frame/methods/test_rename.py | 31 +++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 126a5e10c0e85..33464a73e7821 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -1236,6 +1236,7 @@ Other - Bug in :meth:`DataFrame.query` where using duplicate column names led to a ``TypeError``. (:issue:`59950`) - Bug in :meth:`DataFrame.query` which raised an exception or produced incorrect results when expressions contained backtick-quoted column names containing the hash character ``#``, backticks, or characters that fall outside the ASCII range (U+0001..U+007F). (:issue:`59285`) (:issue:`49633`) - Bug in :meth:`DataFrame.query` which raised an exception when querying integer column names using backticks. (:issue:`60494`) +- Bug in :meth:`DataFrame.rename` and :meth:`Series.rename` when passed a ``mapper``, ``index``, or ``columns`` argument that is a :class:`Series` with non-unique ``ser.index`` producing a corrupted result instead of raising ``ValueError`` (:issue:`58621`) - Bug in :meth:`DataFrame.sample` with ``replace=False`` and ``(n * max(weights) / sum(weights)) > 1``, the method would return biased results. Now raises ``ValueError``. (:issue:`61516`) - Bug in :meth:`DataFrame.shift` where passing a ``freq`` on a DataFrame with no columns did not shift the index correctly. (:issue:`60102`) - Bug in :meth:`DataFrame.sort_index` when passing ``axis="columns"`` and ``ignore_index=True`` and ``ascending=False`` not returning a :class:`RangeIndex` columns (:issue:`57293`) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index aea7a2231582a..b542ca1f431c3 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1054,6 +1054,10 @@ def _rename( if level is not None: level = ax._get_level_number(level) + if isinstance(replacements, ABCSeries) and not replacements.index.is_unique: + # GH#58621 + raise ValueError("Cannot rename with a Series with non-unique index.") + # GH 13473 if not callable(replacements): if ax._is_multi and level is not None: diff --git a/pandas/tests/frame/methods/test_rename.py b/pandas/tests/frame/methods/test_rename.py index 6153a168476d4..8c02e28bc138c 100644 --- a/pandas/tests/frame/methods/test_rename.py +++ b/pandas/tests/frame/methods/test_rename.py @@ -8,6 +8,7 @@ DataFrame, Index, MultiIndex, + Series, merge, ) import pandas._testing as tm @@ -409,3 +410,33 @@ def test_rename_boolean_index(self): index=["foo", "bar", "bah"], ) tm.assert_frame_equal(res, exp) + + def test_rename_non_unique_index_series(self): + # GH#58621 + df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) + orig = df.copy(deep=True) + + rename_series = Series(["X", "Y", "Z", "W"], index=["A", "B", "B", "C"]) + + msg = "Cannot rename with a Series with non-unique index" + with pytest.raises(ValueError, match=msg): + df.rename(rename_series) + with pytest.raises(ValueError, match=msg): + df.rename(columns=rename_series) + with pytest.raises(ValueError, match=msg): + df.rename(columns=rename_series, inplace=True) + + # check we didn't corrupt the original + tm.assert_frame_equal(df, orig) + + # Check the Series method while we're here + ser = df.iloc[0] + with pytest.raises(ValueError, match=msg): + ser.rename(rename_series) + with pytest.raises(ValueError, match=msg): + ser.rename(index=rename_series) + with pytest.raises(ValueError, match=msg): + ser.rename(index=rename_series, inplace=True) + + # check we didn't corrupt the original + tm.assert_series_equal(ser, orig.iloc[0])