diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 3f1ac9bb18f75..c96144ef627d9 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -722,7 +722,7 @@ Reshaping Sparse ^^^^^^ -- +- Bug in :meth:`Series.astype` when converting a ``SparseDtype`` with ``datetime64[ns]`` subtype to ``int64`` dtype raising, inconsistent with the non-sparse behavior (:issue:`49631`) - ExtensionArray diff --git a/pandas/core/arrays/sparse/array.py b/pandas/core/arrays/sparse/array.py index 4c5dbeb6eedf9..00631679e4fda 100644 --- a/pandas/core/arrays/sparse/array.py +++ b/pandas/core/arrays/sparse/array.py @@ -49,7 +49,10 @@ validate_insert_loc, ) -from pandas.core.dtypes.astype import astype_nansafe +from pandas.core.dtypes.astype import ( + astype_array, + astype_nansafe, +) from pandas.core.dtypes.cast import ( construct_1d_arraylike_from_scalar, find_common_type, @@ -90,6 +93,7 @@ from pandas.core.base import PandasObject import pandas.core.common as com from pandas.core.construction import ( + ensure_wrapped_if_datetimelike, extract_array, sanitize_array, ) @@ -120,8 +124,6 @@ class ellipsis(Enum): SparseIndexKind = Literal["integer", "block"] - from pandas.core.dtypes.dtypes import ExtensionDtype - from pandas import Series else: @@ -1305,24 +1307,16 @@ def astype(self, dtype: AstypeArg | None = None, copy: bool = True): future_dtype = pandas_dtype(dtype) if not isinstance(future_dtype, SparseDtype): # GH#34457 - if isinstance(future_dtype, np.dtype): - values = np.array(self) - return astype_nansafe(values, dtype=future_dtype) - else: - # pylint: disable-next=used-before-assignment - dtype = cast(ExtensionDtype, dtype) - cls = dtype.construct_array_type() - return cls._from_sequence(self, dtype=dtype, copy=copy) + values = np.asarray(self) + values = ensure_wrapped_if_datetimelike(values) + return astype_array(values, dtype=future_dtype, copy=False) dtype = self.dtype.update_dtype(dtype) subtype = pandas_dtype(dtype._subtype_with_str) + subtype = cast(np.dtype, subtype) # ensured by update_dtype sp_values = astype_nansafe(self.sp_values, subtype, copy=copy) - # error: Argument 1 to "_simple_new" of "SparseArray" has incompatible type - # "ExtensionArray"; expected "ndarray" - return self._simple_new( - sp_values, self.sp_index, dtype # type: ignore[arg-type] - ) + return self._simple_new(sp_values, self.sp_index, dtype) def map(self: SparseArrayT, mapper) -> SparseArrayT: """ diff --git a/pandas/tests/arrays/sparse/test_astype.py b/pandas/tests/arrays/sparse/test_astype.py index 8751b9bb294ae..924f7a56e806a 100644 --- a/pandas/tests/arrays/sparse/test_astype.py +++ b/pandas/tests/arrays/sparse/test_astype.py @@ -110,3 +110,12 @@ def test_astype_copy_false(self): result = arr.astype(dtype, copy=False) expected = SparseArray([1.0, 2.0, 3.0], fill_value=0.0) tm.assert_sp_array_equal(result, expected) + + def test_astype_dt64_to_int64(self): + # GH#49631 match non-sparse behavior + values = np.array(["NaT", "2016-01-02", "2016-01-03"], dtype="M8[ns]") + + arr = SparseArray(values) + result = arr.astype("int64") + expected = values.astype("int64") + tm.assert_numpy_array_equal(result, expected)