diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 372f991d96a22..4c0acaf667753 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -538,6 +538,7 @@ Other Deprecations - Deprecated passing ``skipna=None`` for :meth:`DataFrame.mad` and :meth:`Series.mad`, pass ``skipna=True`` instead (:issue:`44580`) - Deprecated :meth:`DateOffset.apply`, use ``offset + other`` instead (:issue:`44522`) - A deprecation warning is now shown for :meth:`DataFrame.to_latex` indicating the arguments signature may change and emulate more the arguments to :meth:`.Styler.to_latex` in future versions (:issue:`44411`) +- Deprecated :meth:`Categorical.replace`, use :meth:`Series.replace` instead (:issue:`44929`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 67dc6ade25254..106ceccab5a32 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -2457,6 +2457,16 @@ def replace(self, to_replace, value, inplace: bool = False): [3, 2, 3, 3] Categories (2, int64): [2, 3] """ + # GH#44929 deprecation + warn( + "Categorical.replace is deprecated and will be removed in a future " + "version. Use Series.replace directly instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + return self._replace(to_replace=to_replace, value=value, inplace=inplace) + + def _replace(self, *, to_replace, value, inplace: bool = False): inplace = validate_bool_kwarg(inplace, "inplace") cat = self if inplace else self.copy() diff --git a/pandas/core/internals/array_manager.py b/pandas/core/internals/array_manager.py index 598974979fefb..93a9e8fbcb1ad 100644 --- a/pandas/core/internals/array_manager.py +++ b/pandas/core/internals/array_manager.py @@ -430,7 +430,7 @@ def replace_list( inplace = validate_bool_kwarg(inplace, "inplace") return self.apply_with_block( - "_replace_list", + "replace_list", src_list=src_list, dest_list=dest_list, inplace=inplace, diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 85aa61142dd39..7056a34c73008 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -651,14 +651,14 @@ def replace( # Note: the checks we do in NDFrame.replace ensure we never get # here with listlike to_replace or value, as those cases - # go through _replace_list + # go through replace_list values = self.values if isinstance(values, Categorical): # TODO: avoid special-casing blk = self if inplace else self.copy() - blk.values.replace(to_replace, value, inplace=True) + blk.values._replace(to_replace=to_replace, value=value, inplace=True) return [blk] regex = should_use_regex(regex, to_replace) @@ -743,7 +743,7 @@ def _replace_regex( return [block] @final - def _replace_list( + def replace_list( self, src_list: Iterable[Any], dest_list: Sequence[Any], @@ -751,7 +751,7 @@ def _replace_list( regex: bool = False, ) -> list[Block]: """ - See BlockManager._replace_list docstring. + See BlockManager.replace_list docstring. """ values = self.values diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index 15efe7cc6e91f..cb18c6cccbc60 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -449,7 +449,7 @@ def replace_list( inplace = validate_bool_kwarg(inplace, "inplace") bm = self.apply( - "_replace_list", + "replace_list", src_list=src_list, dest_list=dest_list, inplace=inplace, diff --git a/pandas/tests/arrays/categorical/test_replace.py b/pandas/tests/arrays/categorical/test_replace.py index b5e1fe030ce1c..fe12e7c7571ea 100644 --- a/pandas/tests/arrays/categorical/test_replace.py +++ b/pandas/tests/arrays/categorical/test_replace.py @@ -63,11 +63,18 @@ def test_replace_categorical(to_replace, value, result, expected_error_msg): # GH#26988 cat = Categorical(["a", "b"]) expected = Categorical(result) - result = cat.replace(to_replace, value) + with tm.assert_produces_warning(FutureWarning, match="Series.replace"): + # GH#44929 replace->_replace + result = cat.replace(to_replace, value) + tm.assert_categorical_equal(result, expected) if to_replace == "b": # the "c" test is supposed to be unchanged with pytest.raises(AssertionError, match=expected_error_msg): # ensure non-inplace call does not affect original tm.assert_categorical_equal(cat, expected) - cat.replace(to_replace, value, inplace=True) + + with tm.assert_produces_warning(FutureWarning, match="Series.replace"): + # GH#44929 replace->_replace + cat.replace(to_replace, value, inplace=True) + tm.assert_categorical_equal(cat, expected) diff --git a/pandas/tests/series/methods/test_replace.py b/pandas/tests/series/methods/test_replace.py index 28a0df99bb2b6..3a55062af618f 100644 --- a/pandas/tests/series/methods/test_replace.py +++ b/pandas/tests/series/methods/test_replace.py @@ -8,7 +8,7 @@ class TestSeriesReplace: - def test_replace(self, datetime_series): + def test_replace(self): N = 100 ser = pd.Series(np.random.randn(N)) ser[0:4] = np.nan @@ -58,6 +58,7 @@ def test_replace(self, datetime_series): assert (ser[6:10] == -1).all() assert (ser[20:30] == -1).all() + def test_replace_nan_with_inf(self): ser = pd.Series([np.nan, 0, np.inf]) tm.assert_series_equal(ser.replace(np.nan, 0), ser.fillna(0)) @@ -67,6 +68,7 @@ def test_replace(self, datetime_series): filled[4] = 0 tm.assert_series_equal(ser.replace(np.inf, 0), filled) + def test_replace_listlike_value_listlike_target(self, datetime_series): ser = pd.Series(datetime_series.index) tm.assert_series_equal(ser.replace(np.nan, 0), ser.fillna(0))