From 1499e88e1af86252797406a4ea9d5eb6f13607fe Mon Sep 17 00:00:00 2001 From: Oleh Kozynets Date: Tue, 17 Nov 2020 21:16:34 +0100 Subject: [PATCH 1/5] Deprecate inplace in Categorical.remove_unused_categories. --- pandas/core/arrays/categorical.py | 20 +++++++++++++------ .../arrays/categorical/test_analytics.py | 2 +- pandas/tests/arrays/categorical/test_api.py | 5 ++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 3dec27d4af1ce..2fdd90a6fbcc6 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -10,6 +10,7 @@ from pandas._config import get_option from pandas._libs import NaT, algos as libalgos, hashtable as htable +from pandas._libs.lib import no_default from pandas._typing import ArrayLike, Dtype, Ordered, Scalar from pandas.compat.numpy import function as nv from pandas.util._decorators import cache_readonly, deprecate_kwarg @@ -1046,7 +1047,7 @@ def remove_categories(self, removals, inplace=False): new_categories, ordered=self.ordered, rename=False, inplace=inplace ) - def remove_unused_categories(self, inplace=False): + def remove_unused_categories(self, inplace=no_default): """ Remove categories which are not used. @@ -1056,10 +1057,12 @@ def remove_unused_categories(self, inplace=False): Whether or not to drop unused categories inplace or return a copy of this categorical with unused categories dropped. + .. deprecated:: 1.2.0 + Returns ------- cat : Categorical or None - Categorical with unused categories dropped or None if ``inplace=True``. + Categorical with unused categories dropped. See Also -------- @@ -1069,8 +1072,14 @@ def remove_unused_categories(self, inplace=False): remove_categories : Remove the specified categories. set_categories : Set the categories to the specified ones. """ - inplace = validate_bool_kwarg(inplace, "inplace") - cat = self if inplace else self.copy() + if inplace is not no_default: + warn( + "The `inplace` parameter in pandas.Categorical.remove_unused_categories" + " is deprecated and will be removed in a future version.", + FutureWarning, stacklevel=2 + ) + + cat = self.copy() idx, inv = np.unique(cat._codes, return_inverse=True) if idx.size != 0 and idx[0] == -1: # na sentinel @@ -1083,8 +1092,7 @@ def remove_unused_categories(self, inplace=False): cat._dtype = new_dtype cat._codes = coerce_indexer_dtype(inv, new_dtype.categories) - if not inplace: - return cat + return cat # ------------------------------------------------------------------ diff --git a/pandas/tests/arrays/categorical/test_analytics.py b/pandas/tests/arrays/categorical/test_analytics.py index 98dcdd1692117..f8362e308cfb5 100644 --- a/pandas/tests/arrays/categorical/test_analytics.py +++ b/pandas/tests/arrays/categorical/test_analytics.py @@ -354,7 +354,7 @@ def test_validate_inplace_raises(self, value): with pytest.raises(ValueError, match=msg): cat.remove_categories(removals=["D", "E", "F"], inplace=value) - with pytest.raises(ValueError, match=msg): + with tm.assert_produces_warning(FutureWarning): cat.remove_unused_categories(inplace=value) with pytest.raises(ValueError, match=msg): diff --git a/pandas/tests/arrays/categorical/test_api.py b/pandas/tests/arrays/categorical/test_api.py index 6fce4b4145ff2..082ab54207420 100644 --- a/pandas/tests/arrays/categorical/test_api.py +++ b/pandas/tests/arrays/categorical/test_api.py @@ -371,9 +371,8 @@ def test_remove_unused_categories(self): tm.assert_index_equal(res.categories, exp_categories_dropped) tm.assert_index_equal(c.categories, exp_categories_all) - res = c.remove_unused_categories(inplace=True) - tm.assert_index_equal(c.categories, exp_categories_dropped) - assert res is None + with tm.assert_produces_warning(FutureWarning): + c.remove_unused_categories(inplace=True) # with NaN values (GH11599) c = Categorical(["a", "b", "c", np.nan], categories=["a", "b", "c", "d", "e"]) From 746852ecb59ab8a56d5308de05d4681792218c2c Mon Sep 17 00:00:00 2001 From: Oleh Kozynets Date: Tue, 17 Nov 2020 21:26:45 +0100 Subject: [PATCH 2/5] Add whatsnew entry. --- doc/source/whatsnew/v1.2.0.rst | 1 + pandas/core/arrays/categorical.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 62da3c0c5cddc..004db48c53f7d 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -458,6 +458,7 @@ Deprecations - :meth:`Categorical.is_dtype_equal` and :meth:`CategoricalIndex.is_dtype_equal` are deprecated, will be removed in a future version (:issue:`37545`) - :meth:`Series.slice_shift` and :meth:`DataFrame.slice_shift` are deprecated, use :meth:`Series.shift` or :meth:`DataFrame.shift` instead (:issue:`37601`) - Partial slicing on unordered :class:`DatetimeIndexes` with keys, which are not in Index is deprecated and will be removed in a future version (:issue:`18531`) +- The ``inplace`` parameter of :meth:`Categorical.remove_unused_categories` is deprecated and will be removed in a future version (:issue:`37643`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 2fdd90a6fbcc6..11691f93b34b0 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1076,7 +1076,8 @@ def remove_unused_categories(self, inplace=no_default): warn( "The `inplace` parameter in pandas.Categorical.remove_unused_categories" " is deprecated and will be removed in a future version.", - FutureWarning, stacklevel=2 + FutureWarning, + stacklevel=2, ) cat = self.copy() From 80f02d3530edbe74c5a52b757f3e90b4fe12f686 Mon Sep 17 00:00:00 2001 From: Oleh Kozynets Date: Wed, 18 Nov 2020 08:59:05 +0100 Subject: [PATCH 3/5] Code review changes. --- pandas/core/arrays/categorical.py | 10 +++++++--- pandas/tests/arrays/categorical/test_analytics.py | 6 ++++-- pandas/tests/arrays/categorical/test_api.py | 6 +++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 11691f93b34b0..ae62386b41584 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1062,7 +1062,7 @@ def remove_unused_categories(self, inplace=no_default): Returns ------- cat : Categorical or None - Categorical with unused categories dropped. + Categorical with unused categories dropped or None if ``inplace=True``. See Also -------- @@ -1079,8 +1079,11 @@ def remove_unused_categories(self, inplace=no_default): FutureWarning, stacklevel=2, ) + else: + inplace = False - cat = self.copy() + inplace = validate_bool_kwarg(inplace, "inplace") + cat = self if inplace else self.copy() idx, inv = np.unique(cat._codes, return_inverse=True) if idx.size != 0 and idx[0] == -1: # na sentinel @@ -1093,7 +1096,8 @@ def remove_unused_categories(self, inplace=no_default): cat._dtype = new_dtype cat._codes = coerce_indexer_dtype(inv, new_dtype.categories) - return cat + if not inplace: + return cat # ------------------------------------------------------------------ diff --git a/pandas/tests/arrays/categorical/test_analytics.py b/pandas/tests/arrays/categorical/test_analytics.py index f8362e308cfb5..7bd7d29ec9703 100644 --- a/pandas/tests/arrays/categorical/test_analytics.py +++ b/pandas/tests/arrays/categorical/test_analytics.py @@ -354,8 +354,10 @@ def test_validate_inplace_raises(self, value): with pytest.raises(ValueError, match=msg): cat.remove_categories(removals=["D", "E", "F"], inplace=value) - with tm.assert_produces_warning(FutureWarning): - cat.remove_unused_categories(inplace=value) + with pytest.raises(ValueError, match=msg): + with tm.assert_produces_warning(FutureWarning): + # issue #37643 inplace kwarg deprecated + cat.remove_unused_categories(inplace=value) with pytest.raises(ValueError, match=msg): cat.sort_values(inplace=value) diff --git a/pandas/tests/arrays/categorical/test_api.py b/pandas/tests/arrays/categorical/test_api.py index 082ab54207420..98b0f978c5f59 100644 --- a/pandas/tests/arrays/categorical/test_api.py +++ b/pandas/tests/arrays/categorical/test_api.py @@ -372,7 +372,11 @@ def test_remove_unused_categories(self): tm.assert_index_equal(c.categories, exp_categories_all) with tm.assert_produces_warning(FutureWarning): - c.remove_unused_categories(inplace=True) + # issue #37643 inplace kwarg deprecated + res = c.remove_unused_categories(inplace=True) + + tm.assert_index_equal(c.categories, exp_categories_dropped) + assert res is None # with NaN values (GH11599) c = Categorical(["a", "b", "c", np.nan], categories=["a", "b", "c", "d", "e"]) From 42d1822969511e9f595f15586d88564795d408c0 Mon Sep 17 00:00:00 2001 From: Oleh Kozynets Date: Wed, 18 Nov 2020 11:42:52 +0100 Subject: [PATCH 4/5] Fix CI. --- pandas/core/arrays/categorical.py | 4 ++-- pandas/tests/series/accessors/test_cat_accessor.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index ae62386b41584..c20b39febdcc0 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1074,8 +1074,8 @@ def remove_unused_categories(self, inplace=no_default): """ if inplace is not no_default: warn( - "The `inplace` parameter in pandas.Categorical.remove_unused_categories" - " is deprecated and will be removed in a future version.", + "The `inplace` parameter in pandas.Categorical.remove_unused_categories " + "is deprecated and will be removed in a future version.", FutureWarning, stacklevel=2, ) diff --git a/pandas/tests/series/accessors/test_cat_accessor.py b/pandas/tests/series/accessors/test_cat_accessor.py index f561ac82a8901..8a4c4d56e264d 100644 --- a/pandas/tests/series/accessors/test_cat_accessor.py +++ b/pandas/tests/series/accessors/test_cat_accessor.py @@ -81,7 +81,10 @@ def test_cat_accessor_updates_on_inplace(self): ser = Series(list("abc")).astype("category") return_value = ser.drop(0, inplace=True) assert return_value is None - return_value = ser.cat.remove_unused_categories(inplace=True) + + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + return_value = ser.cat.remove_unused_categories(inplace=True) + assert return_value is None assert len(ser.cat.categories) == 2 From d1ecca0cb6682c63ea92c1206dcb9d771bdee89a Mon Sep 17 00:00:00 2001 From: Oleh Kozynets Date: Wed, 18 Nov 2020 11:57:08 +0100 Subject: [PATCH 5/5] Fix line length. --- pandas/core/arrays/categorical.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index c20b39febdcc0..e57ef17272a9d 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1074,8 +1074,9 @@ def remove_unused_categories(self, inplace=no_default): """ if inplace is not no_default: warn( - "The `inplace` parameter in pandas.Categorical.remove_unused_categories " - "is deprecated and will be removed in a future version.", + "The `inplace` parameter in pandas.Categorical." + "remove_unused_categories is deprecated and " + "will be removed in a future version.", FutureWarning, stacklevel=2, )