Skip to content

Commit 7c208c8

Browse files
authored
BUG: DatetimeArray.astype(Sparse) (#50082)
* BUG: DatetimeArray.astype(Sparse) * GH ref
1 parent 5206bb5 commit 7c208c8

File tree

3 files changed

+33
-17
lines changed

3 files changed

+33
-17
lines changed

doc/source/whatsnew/v2.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,7 @@ Reshaping
791791
Sparse
792792
^^^^^^
793793
- 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`)
794+
- Bug in :meth:`Series.astype` when converting a from ``datetime64[ns]`` to ``Sparse[datetime64[ns]]`` incorrectly raising (:issue:`50082`)
794795
-
795796

796797
ExtensionArray

pandas/core/arrays/datetimes.py

+23-17
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@
7474
is_timedelta64_dtype,
7575
pandas_dtype,
7676
)
77-
from pandas.core.dtypes.dtypes import DatetimeTZDtype
77+
from pandas.core.dtypes.dtypes import (
78+
DatetimeTZDtype,
79+
ExtensionDtype,
80+
)
7881
from pandas.core.dtypes.missing import isna
7982

8083
from pandas.core.arrays import datetimelike as dtl
@@ -645,6 +648,25 @@ def astype(self, dtype, copy: bool = True):
645648
return self.copy()
646649
return self
647650

651+
elif isinstance(dtype, ExtensionDtype):
652+
if not isinstance(dtype, DatetimeTZDtype):
653+
# e.g. Sparse[datetime64[ns]]
654+
return super().astype(dtype, copy=copy)
655+
elif self.tz is None:
656+
# pre-2.0 this did self.tz_localize(dtype.tz), which did not match
657+
# the Series behavior which did
658+
# values.tz_localize("UTC").tz_convert(dtype.tz)
659+
raise TypeError(
660+
"Cannot use .astype to convert from timezone-naive dtype to "
661+
"timezone-aware dtype. Use obj.tz_localize instead or "
662+
"series.dt.tz_localize instead"
663+
)
664+
else:
665+
# tzaware unit conversion e.g. datetime64[s, UTC]
666+
np_dtype = np.dtype(dtype.str)
667+
res_values = astype_overflowsafe(self._ndarray, np_dtype, copy=copy)
668+
return type(self)._simple_new(res_values, dtype=dtype, freq=self.freq)
669+
648670
elif (
649671
self.tz is None
650672
and is_datetime64_dtype(dtype)
@@ -656,22 +678,6 @@ def astype(self, dtype, copy: bool = True):
656678
return type(self)._simple_new(res_values, dtype=res_values.dtype)
657679
# TODO: preserve freq?
658680

659-
elif self.tz is not None and isinstance(dtype, DatetimeTZDtype):
660-
# tzaware unit conversion e.g. datetime64[s, UTC]
661-
np_dtype = np.dtype(dtype.str)
662-
res_values = astype_overflowsafe(self._ndarray, np_dtype, copy=copy)
663-
return type(self)._simple_new(res_values, dtype=dtype, freq=self.freq)
664-
665-
elif self.tz is None and isinstance(dtype, DatetimeTZDtype):
666-
# pre-2.0 this did self.tz_localize(dtype.tz), which did not match
667-
# the Series behavior which did
668-
# values.tz_localize("UTC").tz_convert(dtype.tz)
669-
raise TypeError(
670-
"Cannot use .astype to convert from timezone-naive dtype to "
671-
"timezone-aware dtype. Use obj.tz_localize instead or "
672-
"series.dt.tz_localize instead"
673-
)
674-
675681
elif self.tz is not None and is_datetime64_dtype(dtype):
676682
# pre-2.0 behavior for DTA/DTI was
677683
# values.tz_convert("UTC").tz_localize(None), which did not match

pandas/tests/arrays/test_datetimes.py

+9
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,15 @@ def test_astype_int(self, dtype):
403403
assert result.dtype == expected_dtype
404404
tm.assert_numpy_array_equal(result, expected)
405405

406+
def test_astype_to_sparse_dt64(self):
407+
# GH#50082
408+
dti = pd.date_range("2016-01-01", periods=4)
409+
dta = dti._data
410+
result = dta.astype("Sparse[datetime64[ns]]")
411+
412+
assert result.dtype == "Sparse[datetime64[ns]]"
413+
assert (result == dta).all()
414+
406415
def test_tz_setter_raises(self):
407416
arr = DatetimeArray._from_sequence(
408417
["2000"], dtype=DatetimeTZDtype(tz="US/Central")

0 commit comments

Comments
 (0)