diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 0f5b4a16d2f01..2bb9905454e77 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -222,6 +222,7 @@ Datetimelike - Bug in :meth:`DataFrame.quantile` with datetime-like dtypes and no rows incorrectly returning ``float64`` dtype instead of retaining datetime-like dtype (:issue:`41544`) - Bug in :func:`to_datetime` with sequences of ``np.str_`` objects incorrectly raising (:issue:`32264`) - Bug in :class:`Timestamp` construction when passing datetime components as positional arguments and ``tzinfo`` as a keyword argument incorrectly raising (:issue:`31929`) +- Bug in :meth:`Index.astype` when casting from object dtype to ``timedelta64[ns]`` dtype incorrectly casting ``np.datetime64("NaT")`` values to ``np.timedelta64("NaT")`` instead of raising (:issue:`45722`) - Timedelta diff --git a/pandas/core/dtypes/astype.py b/pandas/core/dtypes/astype.py index 7c7b7f3ba097f..11b147e207327 100644 --- a/pandas/core/dtypes/astype.py +++ b/pandas/core/dtypes/astype.py @@ -15,6 +15,7 @@ import numpy as np from pandas._libs import lib +from pandas._libs.tslibs.timedeltas import array_to_timedelta64 from pandas._typing import ( ArrayLike, DtypeObj, @@ -139,7 +140,7 @@ def astype_nansafe( elif is_object_dtype(arr.dtype): # work around NumPy brokenness, #1987 - if np.issubdtype(dtype.type, np.integer): + if dtype.kind in ["i", "u"]: return lib.astype_intsafe(arr, dtype) # if we have a datetime/timedelta array of objects @@ -154,9 +155,10 @@ def astype_nansafe( copy=copy, ) elif is_timedelta64_dtype(dtype): - from pandas import to_timedelta - - return astype_nansafe(to_timedelta(arr)._values, dtype, copy=copy) + # bc we know arr.dtype == object, this is equivalent to + # `np.asarray(to_timedelta(arr))`, but using a lower-level API that + # does not require a circular import. + return array_to_timedelta64(arr).view("m8[ns]").astype(dtype, copy=False) if dtype.name in ("datetime64", "timedelta64"): msg = ( diff --git a/pandas/tests/indexes/object/test_astype.py b/pandas/tests/indexes/object/test_astype.py index 9bfc0c1312200..91e266e805868 100644 --- a/pandas/tests/indexes/object/test_astype.py +++ b/pandas/tests/indexes/object/test_astype.py @@ -1,4 +1,9 @@ -from pandas import Index +import pytest + +from pandas import ( + Index, + NaT, +) import pandas._testing as tm @@ -8,3 +13,12 @@ def test_astype_str_from_bytes(): result = idx.astype(str) expected = Index(["あ", "a"], dtype="object") tm.assert_index_equal(result, expected) + + +def test_astype_invalid_nas_to_tdt64_raises(): + # GH#45722 don't cast np.datetime64 NaTs to timedelta64 NaT + idx = Index([NaT.asm8] * 2, dtype=object) + + msg = r"Cannot cast Index to dtype timedelta64\[ns\]" + with pytest.raises(TypeError, match=msg): + idx.astype("m8[ns]")