Skip to content

Commit 01b199d

Browse files
committed
deprecate Categorical.map(na_action=ignore)
1 parent 84beb35 commit 01b199d

File tree

6 files changed

+47
-7
lines changed

6 files changed

+47
-7
lines changed

doc/source/whatsnew/v2.1.0.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ enhancement2
2828

2929
Other enhancements
3030
^^^^^^^^^^^^^^^^^^
31-
- :meth:`Categorical.map` and :meth:`CategoricalIndex.map` now have a ``na_action`` parameter (:issue:`44279`)
31+
- :meth:`Categorical.map` and :meth:`CategoricalIndex.map` now have a ``na_action`` parameter.
32+
:meth:`Categorical.map` implicitly had a default value of ``"ignore"`` for ``na_action``. This has formally been deprecated and will be changed to ``None`` in the future.
33+
Also notice that :meth:`Series.map` has default ``na_action=None`` and calls to series with categorical data will now use ``na_action=None`` unless explicitly set otherwise (:issue:`44279`)
3234
- Implemented ``__pandas_priority__`` to allow custom types to take precedence over :class:`DataFrame`, :class:`Series`, :class:`Index`, or :class:`ExtensionArray` for arithmetic operations, :ref:`see the developer guide <extending.pandas_priority>` (:issue:`48347`)
3335
- :meth:`MultiIndex.sort_values` now supports ``na_position`` (:issue:`51612`)
3436
- :meth:`MultiIndex.sortlevel` and :meth:`Index.sortlevel` gained a new keyword ``na_position`` (:issue:`51612`)

pandas/core/apply.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
from pandas.core.dtypes.cast import is_nested_object
4141
from pandas.core.dtypes.common import (
42+
is_categorical_dtype,
4243
is_dict_like,
4344
is_extension_array_dtype,
4445
is_list_like,
@@ -1082,7 +1083,11 @@ def apply_standard(self) -> DataFrame | Series:
10821083
return f(obj)
10831084

10841085
# row-wise access
1085-
mapped = obj._map_values(mapper=f, convert=self.convert_dtype)
1086+
# apply doesn't have a `na_action` keyword and for backward compat reasons
1087+
# we need to give `na_action="ignore"` for categorical data.
1088+
# TODO: remove the `na_action="ignore"` has been removed from Categorical.
1089+
action = "ignore" if is_categorical_dtype(obj) else None
1090+
mapped = obj._map_values(mapper=f, na_action=action, convert=self.convert_dtype)
10861091

10871092
if len(mapped) and isinstance(mapped[0], ABCSeries):
10881093
# GH#43986 Need to do list(mapped) in order to get treated as nested

pandas/core/arrays/categorical.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,11 @@ def remove_unused_categories(self) -> Categorical:
12051205

12061206
# ------------------------------------------------------------------
12071207

1208-
def map(self, mapper, na_action: Literal["ignore"] | None = "ignore"):
1208+
def map(
1209+
self,
1210+
mapper,
1211+
na_action: Literal["ignore"] | None | lib.NoDefault = lib.no_default,
1212+
):
12091213
"""
12101214
Map categories using an input mapping or function.
12111215
@@ -1226,6 +1230,11 @@ def map(self, mapper, na_action: Literal["ignore"] | None = "ignore"):
12261230
If 'ignore', propagate NaN values, without passing them to the
12271231
mapping correspondence.
12281232
1233+
.. deprecated:: 2.1.0
1234+
1235+
The dault value of 'ignore' has been deprecated and will be changed to
1236+
None in the future.
1237+
12291238
Returns
12301239
-------
12311240
pandas.Categorical or pandas.Index
@@ -1277,6 +1286,17 @@ def map(self, mapper, na_action: Literal["ignore"] | None = "ignore"):
12771286
>>> cat.map({'a': 'first', 'b': 'second'})
12781287
Index(['first', 'second', nan], dtype='object')
12791288
"""
1289+
if na_action is lib.no_default:
1290+
warn(
1291+
"The default value of 'ignore' for the `na_action` parameter in "
1292+
"pandas.Categorical.map is deprecated and will be "
1293+
"changed to 'None' in a future version. Please set na_action to the "
1294+
"desired value to avoid seeing this warning",
1295+
FutureWarning,
1296+
stacklevel=find_stack_level(),
1297+
)
1298+
na_action = "ignore"
1299+
12801300
assert callable(mapper) or is_dict_like(mapper)
12811301

12821302
new_categories = self.categories.map(mapper)

pandas/tests/arrays/categorical/test_analytics.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,16 @@ def test_memory_usage(self):
300300

301301
def test_map(self):
302302
c = Categorical(list("ABABC"), categories=list("CBA"), ordered=True)
303-
result = c.map(lambda x: x.lower())
303+
result = c.map(lambda x: x.lower(), na_action=None)
304304
exp = Categorical(list("ababc"), categories=list("cba"), ordered=True)
305305
tm.assert_categorical_equal(result, exp)
306306

307307
c = Categorical(list("ABABC"), categories=list("ABC"), ordered=False)
308-
result = c.map(lambda x: x.lower())
308+
result = c.map(lambda x: x.lower(), na_action=None)
309309
exp = Categorical(list("ababc"), categories=list("abc"), ordered=False)
310310
tm.assert_categorical_equal(result, exp)
311311

312-
result = c.map(lambda x: 1)
312+
result = c.map(lambda x: 1, na_action=None)
313313
# GH 12766: Return an index not an array
314314
tm.assert_index_equal(result, Index(np.array([1] * 5, dtype=np.int64)))
315315

pandas/tests/arrays/categorical/test_map.py

+13
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,16 @@ def test_map_with_dict_or_series(na_action):
139139
result = cat.map(mapper, na_action=na_action)
140140
# Order of categories in result can be different
141141
tm.assert_categorical_equal(result, expected)
142+
143+
144+
def test_map_na_action_no_default_deprecated():
145+
# GH51645
146+
cat = Categorical(["a", "b", "c"])
147+
msg = (
148+
"The default value of 'ignore' for the `na_action` parameter in "
149+
"pandas.Categorical.map is deprecated and will be "
150+
"changed to 'None' in a future version. Please set na_action to the "
151+
"desired value to avoid seeing this warning"
152+
)
153+
with tm.assert_produces_warning(FutureWarning, match=msg):
154+
cat.map(lambda x: x)

pandas/tests/arrays/categorical/test_subclass.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def test_from_codes(self):
1616

1717
def test_map(self):
1818
sc = tm.SubclassedCategorical(["a", "b", "c"])
19-
res = sc.map(lambda x: x.upper())
19+
res = sc.map(lambda x: x.upper(), na_action=None)
2020
assert isinstance(res, tm.SubclassedCategorical)
2121
exp = Categorical(["A", "B", "C"])
2222
tm.assert_categorical_equal(res, exp)

0 commit comments

Comments
 (0)