Skip to content

Commit 42a43e3

Browse files
authored
differences in Series.map with defaultdict with different dtypes (#49011)
1 parent e3d0126 commit 42a43e3

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

doc/source/whatsnew/v2.0.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ Indexing
222222
Missing
223223
^^^^^^^
224224
- Bug in :meth:`Index.equals` raising ``TypeError`` when :class:`Index` consists of tuples that contain ``NA`` (:issue:`48446`)
225-
-
225+
- Bug in :meth:`Series.map` caused incorrect result when data has NaNs and defaultdict mapping was used (:issue:`48813`)
226226

227227
MultiIndex
228228
^^^^^^^^^^

pandas/core/base.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,9 @@ def _map_values(self, mapper, na_action=None):
830830
# If a dictionary subclass defines a default value method,
831831
# convert mapper to a lookup function (GH #15999).
832832
dict_with_default = mapper
833-
mapper = lambda x: dict_with_default[x]
833+
mapper = lambda x: dict_with_default[
834+
np.nan if isinstance(x, float) and np.isnan(x) else x
835+
]
834836
else:
835837
# Dictionary does not have a default. Thus it's safe to
836838
# convert to an Series for efficiency.

pandas/tests/apply/test_series_apply.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,36 @@ def test_map_dict_na_key():
598598
tm.assert_series_equal(result, expected)
599599

600600

601+
@pytest.mark.parametrize("na_action", [None, "ignore"])
602+
def test_map_defaultdict_na_key(na_action):
603+
# GH 48813
604+
s = Series([1, 2, np.nan])
605+
default_map = defaultdict(lambda: "missing", {1: "a", 2: "b", np.nan: "c"})
606+
result = s.map(default_map, na_action=na_action)
607+
expected = Series({0: "a", 1: "b", 2: "c" if na_action is None else np.nan})
608+
tm.assert_series_equal(result, expected)
609+
610+
611+
@pytest.mark.parametrize("na_action", [None, "ignore"])
612+
def test_map_defaultdict_missing_key(na_action):
613+
# GH 48813
614+
s = Series([1, 2, np.nan])
615+
default_map = defaultdict(lambda: "missing", {1: "a", 2: "b", 3: "c"})
616+
result = s.map(default_map, na_action=na_action)
617+
expected = Series({0: "a", 1: "b", 2: "missing" if na_action is None else np.nan})
618+
tm.assert_series_equal(result, expected)
619+
620+
621+
@pytest.mark.parametrize("na_action", [None, "ignore"])
622+
def test_map_defaultdict_unmutated(na_action):
623+
# GH 48813
624+
s = Series([1, 2, np.nan])
625+
default_map = defaultdict(lambda: "missing", {1: "a", 2: "b", np.nan: "c"})
626+
expected_default_map = default_map.copy()
627+
s.map(default_map, na_action=na_action)
628+
assert default_map == expected_default_map
629+
630+
601631
@pytest.mark.parametrize("arg_func", [dict, Series])
602632
def test_map_dict_ignore_na(arg_func):
603633
# GH#47527
@@ -613,7 +643,7 @@ def test_map_defaultdict_ignore_na():
613643
mapping = defaultdict(int, {1: 10, np.nan: 42})
614644
ser = Series([1, np.nan, 2])
615645
result = ser.map(mapping)
616-
expected = Series([10, 0, 0])
646+
expected = Series([10, 42, 0])
617647
tm.assert_series_equal(result, expected)
618648

619649

0 commit comments

Comments
 (0)