Skip to content

Commit aff0694

Browse files
authored
BUG: allow Series[Period].astype(dt64) (#45055)
1 parent f1b255d commit aff0694

File tree

7 files changed

+24
-26
lines changed

7 files changed

+24
-26
lines changed

doc/source/user_guide/timeseries.rst

+3-8
Original file line numberDiff line numberDiff line change
@@ -2102,19 +2102,14 @@ The ``period`` dtype can be used in ``.astype(...)``. It allows one to change th
21022102
# change monthly freq to daily freq
21032103
pi.astype("period[D]")
21042104
2105+
# convert to DatetimeIndex
2106+
pi.astype("datetime64[ns]")
2107+
21052108
# convert to PeriodIndex
21062109
dti = pd.date_range("2011-01-01", freq="M", periods=3)
21072110
dti
21082111
dti.astype("period[M]")
21092112
2110-
.. deprecated:: 1.4.0
2111-
Converting PeriodIndex to DatetimeIndex with ``.astype(...)`` is deprecated and will raise in a future version. Use ``obj.to_timestamp(how).tz_localize(dtype.tz)`` instead.
2112-
2113-
.. ipython:: python
2114-
2115-
# convert to DatetimeIndex
2116-
pi.to_timestamp(how="start")
2117-
21182113
PeriodIndex partial string indexing
21192114
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21202115

doc/source/whatsnew/v1.4.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,6 @@ Other Deprecations
537537
- Deprecated casting behavior when passing an item with mismatched-timezone to :meth:`DatetimeIndex.insert`, :meth:`DatetimeIndex.putmask`, :meth:`DatetimeIndex.where` :meth:`DatetimeIndex.fillna`, :meth:`Series.mask`, :meth:`Series.where`, :meth:`Series.fillna`, :meth:`Series.shift`, :meth:`Series.replace`, :meth:`Series.reindex` (and :class:`DataFrame` column analogues). In the past this has cast to object dtype. In a future version, these will cast the passed item to the index or series's timezone (:issue:`37605`,:issue:`44940`)
538538
- Deprecated the 'errors' keyword argument in :meth:`Series.where`, :meth:`DataFrame.where`, :meth:`Series.mask`, and :meth:`DataFrame.mask`; in a future version the argument will be removed (:issue:`44294`)
539539
- Deprecated the ``prefix`` keyword argument in :func:`read_csv` and :func:`read_table`, in a future version the argument will be removed (:issue:`43396`)
540-
- Deprecated :meth:`PeriodIndex.astype` to ``datetime64[ns]`` or ``DatetimeTZDtype``, use ``obj.to_timestamp(how).tz_localize(dtype.tz)`` instead (:issue:`44398`)
541540
- Deprecated passing non boolean argument to sort in :func:`concat` (:issue:`41518`)
542541
- Deprecated passing arguments as positional for :func:`read_fwf` other than ``filepath_or_buffer`` (:issue:`41485`):
543542
- Deprecated passing ``skipna=None`` for :meth:`DataFrame.mad` and :meth:`Series.mad`, pass ``skipna=True`` instead (:issue:`44580`)
@@ -676,6 +675,7 @@ Conversion
676675
- Bug in :meth:`DataFrame.astype` not propagating ``attrs`` from the original :class:`DataFrame` (:issue:`44414`)
677676
- Bug in :meth:`DataFrame.convert_dtypes` result losing ``columns.names`` (:issue:`41435`)
678677
- Bug in constructing a ``IntegerArray`` from pyarrow data failing to validate dtypes (:issue:`44891`)
678+
- Bug in :meth:`Series.astype` not allowing converting from a ``PeriodDtype`` to ``datetime64`` dtype, inconsistent with the :class:`PeriodIndex` behavior (:issue:`45038`)
679679
-
680680

681681
Strings

pandas/core/arrays/period.py

+7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from pandas.core.dtypes.common import (
5454
TD64NS_DTYPE,
5555
ensure_object,
56+
is_datetime64_any_dtype,
5657
is_datetime64_dtype,
5758
is_dtype_equal,
5859
is_float_dtype,
@@ -666,6 +667,12 @@ def astype(self, dtype, copy: bool = True):
666667
return self.copy()
667668
if is_period_dtype(dtype):
668669
return self.asfreq(dtype.freq)
670+
671+
if is_datetime64_any_dtype(dtype):
672+
# GH#45038 match PeriodIndex behavior.
673+
tz = getattr(dtype, "tz", None)
674+
return self.to_timestamp().tz_localize(tz)
675+
669676
return super().astype(dtype, copy=copy)
670677

671678
def searchsorted(

pandas/core/indexes/period.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -358,14 +358,9 @@ def astype(self, dtype, copy: bool = True, how=lib.no_default):
358358

359359
if is_datetime64_any_dtype(dtype):
360360
# 'how' is index-specific, isn't part of the EA interface.
361-
# GH#44398 deprecate astype(dt64), matching Series behavior
362-
warnings.warn(
363-
f"Converting {type(self).__name__} to DatetimeIndex with "
364-
"'astype' is deprecated and will raise in a future version. "
365-
"Use `obj.to_timestamp(how).tz_localize(dtype.tz)` instead.",
366-
FutureWarning,
367-
stacklevel=find_stack_level(),
368-
)
361+
# GH#45038 implement this for PeriodArray (but without "how")
362+
# once the "how" deprecation is enforced we can just dispatch
363+
# directly to PeriodArray.
369364
tz = getattr(dtype, "tz", None)
370365
return self.to_timestamp(how=how).tz_localize(tz)
371366

pandas/tests/arrays/period/test_astype.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,12 @@ def test_astype_period():
6666
def test_astype_datetime(other):
6767
arr = period_array(["2000", "2001", None], freq="D")
6868
# slice off the [ns] so that the regex matches.
69-
with pytest.raises(TypeError, match=other[:-4]):
70-
arr.astype(other)
69+
if other == "timedelta64[ns]":
70+
with pytest.raises(TypeError, match=other[:-4]):
71+
arr.astype(other)
72+
73+
else:
74+
# GH#45038 allow period->dt64 because we allow dt64->period
75+
result = arr.astype(other)
76+
expected = pd.DatetimeIndex(["2000", "2001", pd.NaT])._data
77+
tm.assert_datetime_array_equal(result, expected)

pandas/tests/indexes/period/methods/test_astype.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,7 @@ def test_period_astype_to_timestamp(self):
164164
assert res.freq == exp.freq
165165

166166
exp = DatetimeIndex(["2011-01-01", "2011-02-01", "2011-03-01"], tz="US/Eastern")
167-
msg = "Use `obj.to_timestamp"
168-
with tm.assert_produces_warning(FutureWarning, match=msg):
169-
# GH#44398
170-
res = pi.astype("datetime64[ns, US/Eastern]")
167+
res = pi.astype("datetime64[ns, US/Eastern]")
171168
tm.assert_index_equal(res, exp)
172169
assert res.freq == exp.freq
173170

pandas/tests/indexes/test_common.py

-3
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,6 @@ def test_astype_preserves_name(self, index, dtype):
393393
):
394394
# This astype is deprecated in favor of tz_localize
395395
warn = FutureWarning
396-
elif isinstance(index, PeriodIndex) and dtype == "datetime64[ns]":
397-
# Deprecated in favor of to_timestamp GH#44398
398-
warn = FutureWarning
399396
try:
400397
# Some of these conversions cannot succeed so we use a try / except
401398
with tm.assert_produces_warning(warn):

0 commit comments

Comments
 (0)