Skip to content

Commit 74ca632

Browse files
committed
deprecate Categorical.map(na_action=ignore)
1 parent d398d93 commit 74ca632

File tree

6 files changed

+49
-7
lines changed

6 files changed

+49
-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,
@@ -1086,7 +1087,11 @@ def apply_standard(self) -> DataFrame | Series:
10861087
return f(obj)
10871088

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

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

pandas/core/arrays/categorical.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
cast,
1414
overload,
1515
)
16+
from warnings import warn
1617

1718
import numpy as np
1819

@@ -25,6 +26,7 @@
2526
)
2627
from pandas._libs.arrays import NDArrayBacked
2728
from pandas.compat.numpy import function as nv
29+
from pandas.util._exceptions import find_stack_level
2830
from pandas.util._validators import validate_bool_kwarg
2931

3032
from pandas.core.dtypes.cast import (
@@ -1196,7 +1198,11 @@ def remove_unused_categories(self) -> Categorical:
11961198

11971199
# ------------------------------------------------------------------
11981200

1199-
def map(self, mapper, na_action: Literal["ignore"] | None = "ignore"):
1201+
def map(
1202+
self,
1203+
mapper,
1204+
na_action: Literal["ignore"] | None | lib.NoDefault = lib.no_default,
1205+
):
12001206
"""
12011207
Map categories using an input mapping or function.
12021208
@@ -1217,6 +1223,11 @@ def map(self, mapper, na_action: Literal["ignore"] | None = "ignore"):
12171223
If 'ignore', propagate NaN values, without passing them to the
12181224
mapping correspondence.
12191225
1226+
.. deprecated:: 2.1.0
1227+
1228+
The dault value of 'ignore' has been deprecated and will be changed to
1229+
None in the future.
1230+
12201231
Returns
12211232
-------
12221233
pandas.Categorical or pandas.Index
@@ -1268,6 +1279,17 @@ def map(self, mapper, na_action: Literal["ignore"] | None = "ignore"):
12681279
>>> cat.map({'a': 'first', 'b': 'second'})
12691280
Index(['first', 'second', nan], dtype='object')
12701281
"""
1282+
if na_action is lib.no_default:
1283+
warn(
1284+
"The default value of 'ignore' for the `na_action` parameter in "
1285+
"pandas.Categorical.map is deprecated and will be "
1286+
"changed to 'None' in a future version. Please set na_action to the "
1287+
"desired value to avoid seeing this warning",
1288+
FutureWarning,
1289+
stacklevel=find_stack_level(),
1290+
)
1291+
na_action = "ignore"
1292+
12711293
assert callable(mapper) or is_dict_like(mapper)
12721294

12731295
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)