Skip to content

Commit b3087ef

Browse files
alanbatojreback
authored andcommittedSep 21, 2017
Allow for dict-like argument to Categorical.rename_categories (#17586)
closes #17336
1 parent 6930f27 commit b3087ef

File tree

4 files changed

+54
-8
lines changed

4 files changed

+54
-8
lines changed
 

‎doc/source/categorical.rst

+4
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ by using the :func:`Categorical.rename_categories` method:
206206
s.cat.categories = ["Group %s" % g for g in s.cat.categories]
207207
s
208208
s.cat.rename_categories([1,2,3])
209+
s
210+
# You can also pass a dict-like object to map the renaming
211+
s.cat.rename_categories({1: 'x', 2: 'y', 3: 'z'})
212+
s
209213
210214
.. note::
211215

‎doc/source/whatsnew/v0.21.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ Other Enhancements
115115
- :func:`DataFrame.items` and :func:`Series.items` is now present in both Python 2 and 3 and is lazy in all cases (:issue:`13918`, :issue:`17213`)
116116
- :func:`Styler.where` has been implemented. It is as a convenience for :func:`Styler.applymap` and enables simple DataFrame styling on the Jupyter notebook (:issue:`17474`).
117117
- :func:`MultiIndex.is_monotonic_decreasing` has been implemented. Previously returned ``False`` in all cases. (:issue:`16554`)
118+
- :func:`Categorical.rename_categories` now accepts a dict-like argument as `new_categories` and only updates the categories found in that dict. (:issue:`17336`)
118119

119120

120121
.. _whatsnew_0210.api_breaking:

‎pandas/core/categorical.py

+15-8
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
is_categorical_dtype,
2626
is_integer_dtype, is_bool,
2727
is_list_like, is_sequence,
28-
is_scalar)
28+
is_scalar,
29+
is_dict_like)
2930
from pandas.core.common import is_null_slice, _maybe_box_datetimelike
3031

3132
from pandas.core.algorithms import factorize, take_1d, unique1d
@@ -792,19 +793,20 @@ def set_categories(self, new_categories, ordered=None, rename=False,
792793
def rename_categories(self, new_categories, inplace=False):
793794
""" Renames categories.
794795
795-
The new categories has to be a list-like object. All items must be
796-
unique and the number of items in the new categories must be the same
797-
as the number of items in the old categories.
796+
The new categories can be either a list-like dict-like object.
797+
If it is list-like, all items must be unique and the number of items
798+
in the new categories must be the same as the number of items in the
799+
old categories.
798800
799801
Raises
800802
------
801803
ValueError
802-
If the new categories do not have the same number of items than the
803-
current categories or do not validate as categories
804+
If new categories are list-like and do not have the same number of
805+
items than the current categories or do not validate as categories
804806
805807
Parameters
806808
----------
807-
new_categories : Index-like
809+
new_categories : Index-like or dict-like (>=0.21.0)
808810
The renamed categories.
809811
inplace : boolean (default: False)
810812
Whether or not to rename the categories inplace or return a copy of
@@ -824,7 +826,12 @@ def rename_categories(self, new_categories, inplace=False):
824826
"""
825827
inplace = validate_bool_kwarg(inplace, 'inplace')
826828
cat = self if inplace else self.copy()
827-
cat.categories = new_categories
829+
830+
if is_dict_like(new_categories):
831+
cat.categories = [new_categories.get(item, item)
832+
for item in cat.categories]
833+
else:
834+
cat.categories = new_categories
828835
if not inplace:
829836
return cat
830837

‎pandas/tests/test_categorical.py

+34
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,40 @@ def test_rename_categories(self):
983983
with pytest.raises(ValueError):
984984
cat.rename_categories([1, 2])
985985

986+
def test_rename_categories_dict(self):
987+
# GH 17336
988+
cat = pd.Categorical(['a', 'b', 'c', 'd'])
989+
res = cat.rename_categories({'a': 4, 'b': 3, 'c': 2, 'd': 1})
990+
expected = Index([4, 3, 2, 1])
991+
tm.assert_index_equal(res.categories, expected)
992+
993+
# Test for inplace
994+
res = cat.rename_categories({'a': 4, 'b': 3, 'c': 2, 'd': 1},
995+
inplace=True)
996+
assert res is None
997+
tm.assert_index_equal(cat.categories, expected)
998+
999+
# Test for dicts of smaller length
1000+
cat = pd.Categorical(['a', 'b', 'c', 'd'])
1001+
res = cat.rename_categories({'a': 1, 'c': 3})
1002+
1003+
expected = Index([1, 'b', 3, 'd'])
1004+
tm.assert_index_equal(res.categories, expected)
1005+
1006+
# Test for dicts with bigger length
1007+
cat = pd.Categorical(['a', 'b', 'c', 'd'])
1008+
res = cat.rename_categories({'a': 1, 'b': 2, 'c': 3,
1009+
'd': 4, 'e': 5, 'f': 6})
1010+
expected = Index([1, 2, 3, 4])
1011+
tm.assert_index_equal(res.categories, expected)
1012+
1013+
# Test for dicts with no items from old categories
1014+
cat = pd.Categorical(['a', 'b', 'c', 'd'])
1015+
res = cat.rename_categories({'f': 1, 'g': 3})
1016+
1017+
expected = Index(['a', 'b', 'c', 'd'])
1018+
tm.assert_index_equal(res.categories, expected)
1019+
9861020
@pytest.mark.parametrize('codes, old, new, expected', [
9871021
([0, 1], ['a', 'b'], ['a', 'b'], [0, 1]),
9881022
([0, 1], ['b', 'a'], ['b', 'a'], [0, 1]),

0 commit comments

Comments
 (0)
Please sign in to comment.