Repository: pandas-dev/pandas

Issue:
BUG/API: DTI/TDI/PI/IntervalIndex.map ignore na_action
Split from #44279, so that issue can be about `Categorical.map` & `CategoricalIndex.map`.


Discussion:

Patch:
diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst
index c6783e46faaee..4e4ac14c37961 100644
--- a/doc/source/whatsnew/v2.1.0.rst
+++ b/doc/source/whatsnew/v2.1.0.rst
@@ -140,13 +140,17 @@ Categorical
 Datetimelike
 ^^^^^^^^^^^^
 - Bug in :meth:`Timestamp.round` with values close to the implementation bounds returning incorrect results instead of raising ``OutOfBoundsDatetime`` (:issue:`51494`)
+- :meth:`arrays.DatetimeArray.map` can now take a ``na_action`` argument. :meth:`DatetimeIndex.map` with ``na_action="ignore"`` now works as expected. (:issue:`51644`)
 - Bug in :meth:`arrays.DatetimeArray.map` and :meth:`DatetimeIndex.map`, where the supplied callable operated array-wise instead of element-wise (:issue:`51977`)
+-
 
 Timedelta
 ^^^^^^^^^
 - Bug in :meth:`Timedelta.round` with values close to the implementation bounds returning incorrect results instead of raising ``OutOfBoundsTimedelta`` (:issue:`51494`)
 - Bug in :class:`TimedeltaIndex` division or multiplication leading to ``.freq`` of "0 Days" instead of ``None`` (:issue:`51575`)
+- :meth:`arrays.TimedeltaArray.map` can now take a ``na_action`` argument. :meth:`TimedeltaIndex.map` with ``na_action="ignore"`` now works as expected. (:issue:`51644`)
 - Bug in :meth:`arrays.TimedeltaArray.map` and :meth:`TimedeltaIndex.map`, where the supplied callable operated array-wise instead of element-wise (:issue:`51977`)
+-
 
 Timezones
 ^^^^^^^^^
@@ -196,6 +200,7 @@ I/O
 Period
 ^^^^^^
 - Bug in :class:`PeriodDtype` constructor failing to raise ``TypeError`` when no argument is passed or when ``None`` is passed (:issue:`27388`)
+- :meth:`arrays.PeriodArray.map` can now take a ``na_action`` argument. :meth:`PeriodIndex.map` with ``na_action="ignore"`` now works as expected. (:issue:`51644`)
 - Bug in :class:`PeriodDtype` constructor raising ``ValueError`` instead of ``TypeError`` when an invalid type is passed (:issue:`51790`)
 - Bug in :meth:`arrays.PeriodArray.map` and :meth:`PeriodIndex.map`, where the supplied callable operated array-wise instead of element-wise (:issue:`51977`)
 -
diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py
index 2d2be0a07c0e0..90771eca6fdf1 100644
--- a/pandas/core/arrays/datetimelike.py
+++ b/pandas/core/arrays/datetimelike.py
@@ -747,12 +747,9 @@ def _unbox(self, other) -> np.int64 | np.datetime64 | np.timedelta64 | np.ndarra
 
     @ravel_compat
     def map(self, mapper, na_action=None):
-        if na_action is not None:
-            raise NotImplementedError
-
         from pandas import Index
 
-        result = map_array(self, mapper)
+        result = map_array(self, mapper, na_action=na_action)
         result = Index(result)
 
         if isinstance(result, ABCMultiIndex):
diff --git a/pandas/core/base.py b/pandas/core/base.py
index dc37550ced387..96209ba97c0aa 100644
--- a/pandas/core/base.py
+++ b/pandas/core/base.py
@@ -895,8 +895,8 @@ def _map_values(self, mapper, na_action=None, convert: bool = True):
             return arr.map(mapper, na_action=na_action)
 
         # Argument 1 to "map_array" has incompatible type
-        # "Union[IndexOpsMixin, ExtensionArray, ndarray[Any, Any]]";
-        # expected "Union[ExtensionArray, ndarray[Any, Any]]"
+        # "Union[IndexOpsMixin, ndarray[Any, Any]]";
+        # expected "Union[ExtensionArray, ndarray[Any, Any]]
         return algorithms.map_array(
             arr, mapper, na_action=na_action, convert=convert  # type: ignore[arg-type]
         )


Test patch:
diff --git a/pandas/tests/apply/test_invalid_arg.py b/pandas/tests/apply/test_invalid_arg.py
index 294693df7340a..0207391c3070a 100644
--- a/pandas/tests/apply/test_invalid_arg.py
+++ b/pandas/tests/apply/test_invalid_arg.py
@@ -83,13 +83,6 @@ def test_map_categorical_na_action():
         s.map(lambda x: x, na_action="ignore")
 
 
-def test_map_datetimetz_na_action():
-    values = date_range("2011-01-01", "2011-01-02", freq="H").tz_localize("Asia/Tokyo")
-    s = Series(values, name="XX")
-    with pytest.raises(NotImplementedError, match=tm.EMPTY_STRING_PATTERN):
-        s.map(lambda x: x, na_action="ignore")
-
-
 @pytest.mark.parametrize("method", ["apply", "agg", "transform"])
 @pytest.mark.parametrize("func", [{"A": {"B": "sum"}}, {"A": {"B": ["sum"]}}])
 def test_nested_renamer(frame_or_series, method, func):
diff --git a/pandas/tests/extension/test_datetime.py b/pandas/tests/extension/test_datetime.py
index 21f4f949cdfa9..a93271a454459 100644
--- a/pandas/tests/extension/test_datetime.py
+++ b/pandas/tests/extension/test_datetime.py
@@ -118,12 +118,8 @@ def test_combine_add(self, data_repeated):
 
     @pytest.mark.parametrize("na_action", [None, "ignore"])
     def test_map(self, data, na_action):
-        if na_action is not None:
-            with pytest.raises(NotImplementedError, match=""):
-                data.map(lambda x: x, na_action=na_action)
-        else:
-            result = data.map(lambda x: x, na_action=na_action)
-            self.assert_extension_array_equal(result, data)
+        result = data.map(lambda x: x, na_action=na_action)
+        self.assert_extension_array_equal(result, data)
 
 
 class TestInterface(BaseDatetimeTests, base.BaseInterfaceTests):
diff --git a/pandas/tests/extension/test_period.py b/pandas/tests/extension/test_period.py
index 4be761358ba15..0c961a8563a6e 100644
--- a/pandas/tests/extension/test_period.py
+++ b/pandas/tests/extension/test_period.py
@@ -107,12 +107,8 @@ def test_diff(self, data, periods):
 
     @pytest.mark.parametrize("na_action", [None, "ignore"])
     def test_map(self, data, na_action):
-        if na_action is not None:
-            with pytest.raises(NotImplementedError, match=""):
-                data.map(lambda x: x, na_action=na_action)
-        else:
-            result = data.map(lambda x: x, na_action=na_action)
-            self.assert_extension_array_equal(result, data)
+        result = data.map(lambda x: x, na_action=na_action)
+        self.assert_extension_array_equal(result, data)
 
 
 class TestInterface(BasePeriodTests, base.BaseInterfaceTests):