diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 5b61695410474..6c497b3a7b6d8 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -2284,7 +2284,16 @@ def _replace(self, *, to_replace, value, inplace: bool = False): ser = ser.replace(to_replace=to_replace, value=value) all_values = Index(ser) - new_categories = Index(ser.drop_duplicates(keep="first")) + + # GH51016: maintain order of existing categories + idxr = cat.categories.get_indexer_for(all_values) + locs = np.arange(len(ser)) + locs = np.where(idxr == -1, locs, idxr) + locs = locs.argsort() + + new_categories = ser.take(locs) + new_categories = new_categories.drop_duplicates(keep="first") + new_categories = Index(new_categories) new_codes = recode_for_categories( cat._codes, all_values, new_categories, copy=False ) diff --git a/pandas/tests/arrays/categorical/test_replace.py b/pandas/tests/arrays/categorical/test_replace.py index c25f1d9c9feac..ee9e1dbc81e12 100644 --- a/pandas/tests/arrays/categorical/test_replace.py +++ b/pandas/tests/arrays/categorical/test_replace.py @@ -78,3 +78,13 @@ def test_replace_categorical_ea_dtype(): result = pd.Series(cat).replace(["a", "b"], ["c", pd.NA])._values expected = Categorical(pd.array(["c", pd.NA], dtype="string")) tm.assert_categorical_equal(result, expected) + + +def test_replace_maintain_ordering(): + # GH51016 + dtype = pd.CategoricalDtype([0, 1, 2], ordered=True) + ser = pd.Series([0, 1, 2], dtype=dtype) + result = ser.replace(0, 2) + expected_dtype = pd.CategoricalDtype([1, 2], ordered=True) + expected = pd.Series([2, 1, 2], dtype=expected_dtype) + tm.assert_series_equal(expected, result, check_category_order=True)