Repository: pandas-dev/pandas
Pull request #58645 (2024-05-08 20:13:37)

CLN: Stopped casting values in Series/Index.isin for datelike dtypes
xref https://github.com/pandas-dev/pandas/issues/53111


Patch:
diff --git a/ci/code_checks.sh b/ci/code_checks.sh
index 4bea3a462d..a194c340c6 100755
--- a/ci/code_checks.sh
+++ b/ci/code_checks.sh
@@ -148,7 +148,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         -i "pandas.Series.cat.set_categories PR01,PR02" \
         -i "pandas.Series.dt.as_unit PR01,PR02" \
         -i "pandas.Series.dt.ceil PR01,PR02" \
-        -i "pandas.Series.dt.components SA01" \
         -i "pandas.Series.dt.day_name PR01,PR02" \
         -i "pandas.Series.dt.floor PR01,PR02" \
         -i "pandas.Series.dt.freq GL08" \
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index 7da6d43c73..731406394e 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -247,6 +247,7 @@ Removal of prior version deprecations/changes
 - Removed the "closed" and "normalize" keywords in :meth:`DatetimeIndex.__new__` (:issue:`52628`)
 - Removed the deprecated ``delim_whitespace`` keyword in :func:`read_csv` and :func:`read_table`, use ``sep=r"\s+"`` instead (:issue:`55569`)
 - Require :meth:`SparseDtype.fill_value` to be a valid value for the :meth:`SparseDtype.subtype` (:issue:`53043`)
+- Stopped automatically casting non-datetimelike values (mainly strings) in :meth:`Series.isin` and :meth:`Index.isin` with ``datetime64``, ``timedelta64``, and :class:`PeriodDtype` dtypes (:issue:`53111`)
 - Stopped performing dtype inference when setting a :class:`Index` into a :class:`DataFrame` (:issue:`56102`)
 - Stopped performing dtype inference with in :meth:`Index.insert` with object-dtype index; this often affects the index/columns that result when setting new entries into an empty :class:`Series` or :class:`DataFrame` (:issue:`51363`)
 - Removed the "closed" and "unit" keywords in :meth:`TimedeltaIndex.__new__` (:issue:`52628`, :issue:`55499`)
diff --git a/pandas/core/accessor.py b/pandas/core/accessor.py
index 6c8246e2d0..3acbfc3eab 100644
--- a/pandas/core/accessor.py
+++ b/pandas/core/accessor.py
@@ -226,6 +226,11 @@ class Accessor:
         return self._accessor(obj)
 
 
+# Alias kept for downstream libraries
+# TODO: Deprecate as name is now misleading
+CachedAccessor = Accessor
+
+
 @doc(klass="", examples="", others="")
 def _register_accessor(
     name: str, cls: type[NDFrame | Index]
diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py
index 26095e0af7..925858a20c 100644
--- a/pandas/core/arrays/datetimelike.py
+++ b/pandas/core/arrays/datetimelike.py
@@ -759,14 +759,6 @@ class DatetimeLikeArrayMixin(  # type: ignore[misc]
         values = ensure_wrapped_if_datetimelike(values)
 
         if not isinstance(values, type(self)):
-            inferable = [
-                "timedelta",
-                "timedelta64",
-                "datetime",
-                "datetime64",
-                "date",
-                "period",
-            ]
             if values.dtype == object:
                 values = lib.maybe_convert_objects(
                     values,  # type: ignore[arg-type]
@@ -775,32 +767,11 @@ class DatetimeLikeArrayMixin(  # type: ignore[misc]
                 )
                 if values.dtype != object:
                     return self.isin(values)
-
-                inferred = lib.infer_dtype(values, skipna=False)
-                if inferred not in inferable:
-                    if inferred == "string":
-                        pass
-
-                    elif "mixed" in inferred:
-                        return isin(self.astype(object), values)
-                    else:
-                        return np.zeros(self.shape, dtype=bool)
-
-            try:
-                values = type(self)._from_sequence(values)
-            except ValueError:
-                return isin(self.astype(object), values)
-            else:
-                warnings.warn(
-                    # GH#53111
-                    f"The behavior of 'isin' with dtype={self.dtype} and "
-                    "castable values (e.g. strings) is deprecated. In a "
-                    "future version, these will not be considered matching "
-                    "by isin. Explicitly cast to the appropriate dtype before "
-                    "calling isin instead.",
-                    FutureWarning,
-                    stacklevel=find_stack_level(),
-                )
+                else:
+                    # TODO: Deprecate this case
+                    # https://github.com/pandas-dev/pandas/pull/58645/files#r1604055791
+                    return isin(self.astype(object), values)
+            return np.zeros(self.shape, dtype=bool)
 
         if self.dtype.kind in "mM":
             self = cast("DatetimeArray | TimedeltaArray", self)
diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py
index 3dcd1fedc8..3cb51f7447 100644
--- a/pandas/core/indexes/accessors.py
+++ b/pandas/core/indexes/accessors.py
@@ -489,10 +489,20 @@ class TimedeltaProperties(Properties):
         """
         Return a Dataframe of the components of the Timedeltas.
 
+        Each row of the DataFrame corresponds to a Timedelta in the original
+        Series and contains the individual components (days, hours, minutes,
+        seconds, milliseconds, microseconds, nanoseconds) of the Timedelta.
+
         Returns
         -------
         DataFrame
 
+        See Also
+        --------
+        TimedeltaIndex.components : Return a DataFrame of the individual resolution
+            components of the Timedeltas.
+        Series.dt.total_seconds : Return the total number of seconds in the duration.
+
         Examples
         --------
         >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="s"))


Test patch:
diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py
index 1b5d33fc10..6da6ad27f8 100644
--- a/pandas/tests/test_algos.py
+++ b/pandas/tests/test_algos.py
@@ -990,21 +990,18 @@ class TestIsin:
         tm.assert_numpy_array_equal(result, expected)
 
     @pytest.mark.parametrize("dtype", ["m8[ns]", "M8[ns]", "M8[ns, UTC]"])
-    def test_isin_datetimelike_strings_deprecated(self, dtype):
+    def test_isin_datetimelike_strings_returns_false(self, dtype):
         # GH#53111
         dta = date_range("2013-01-01", periods=3)._values
         arr = Series(dta.view("i8")).array.view(dtype)
 
         vals = [str(x) for x in arr]
-        msg = "The behavior of 'isin' with dtype=.* is deprecated"
-        with tm.assert_produces_warning(FutureWarning, match=msg):
-            res = algos.isin(arr, vals)
-        assert res.all()
+        res = algos.isin(arr, vals)
+        assert not res.any()
 
         vals2 = np.array(vals, dtype=str)
-        with tm.assert_produces_warning(FutureWarning, match=msg):
-            res2 = algos.isin(arr, vals2)
-        assert res2.all()
+        res2 = algos.isin(arr, vals2)
+        assert not res2.any()
 
     def test_isin_dt64tz_with_nat(self):
         # the all-NaT values used to get inferred to tznaive, which was evaluated