Skip to content

BUG: Series[object].astype(td64_unsupported) #50070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions pandas/core/dtypes/astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import inspect
from typing import (
TYPE_CHECKING,
cast,
overload,
)

Expand Down Expand Up @@ -36,7 +37,11 @@
from pandas.core.dtypes.missing import isna

if TYPE_CHECKING:
from pandas.core.arrays import ExtensionArray
from pandas.core.arrays import (
DatetimeArray,
ExtensionArray,
TimedeltaArray,
)


_dtype_obj = np.dtype(object)
Expand Down Expand Up @@ -109,7 +114,11 @@ def astype_nansafe(

# allow frequency conversions
if dtype.kind == "M":
return arr.astype(dtype)
from pandas.core.construction import ensure_wrapped_if_datetimelike

dta = ensure_wrapped_if_datetimelike(arr)
dta = cast("DatetimeArray", dta)
return dta.astype(dtype, copy=copy)._ndarray

raise TypeError(f"cannot astype a datetimelike from [{arr.dtype}] to [{dtype}]")

Expand All @@ -124,8 +133,9 @@ def astype_nansafe(
# and doing the old convert-to-float behavior otherwise.
from pandas.core.construction import ensure_wrapped_if_datetimelike

arr = ensure_wrapped_if_datetimelike(arr)
return arr.astype(dtype, copy=copy)
tda = ensure_wrapped_if_datetimelike(arr)
tda = cast("TimedeltaArray", tda)
return tda.astype(dtype, copy=copy)._ndarray

raise TypeError(f"cannot astype a timedelta from [{arr.dtype}] to [{dtype}]")

Expand All @@ -145,10 +155,15 @@ def astype_nansafe(
return dta.astype(dtype, copy=False)._ndarray

elif is_timedelta64_dtype(dtype):
from pandas.core.construction import ensure_wrapped_if_datetimelike

# 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)
tdvals = array_to_timedelta64(arr).view("m8[ns]")

tda = ensure_wrapped_if_datetimelike(tdvals)
return tda.astype(dtype, copy=False)._ndarray

if dtype.name in ("datetime64", "timedelta64"):
msg = (
Expand Down
16 changes: 16 additions & 0 deletions pandas/tests/frame/methods/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,22 @@ def test_astype_from_object_to_datetime_unit(self, unit):
with pytest.raises(TypeError, match="Cannot cast"):
df.astype(f"M8[{unit}]")

@pytest.mark.parametrize("unit", ["Y", "M", "W", "D", "h", "m"])
def test_astype_from_object_to_timedelta_unit(self, unit):
vals = [
["1 Day", "2 Days", "3 Days"],
["4 Days", "5 Days", "6 Days"],
]
df = DataFrame(vals, dtype=object)
msg = (
r"Cannot convert from timedelta64\[ns\] to timedelta64\[.*\]. "
"Supported resolutions are 's', 'ms', 'us', 'ns'"
)
with pytest.raises(ValueError, match=msg):
# TODO: this is ValueError while for DatetimeArray it is TypeError;
# get these consistent
df.astype(f"m8[{unit}]")

@pytest.mark.parametrize("dtype", ["M8", "m8"])
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s", "h", "m", "D"])
def test_astype_from_datetimelike_to_object(self, dtype, unit):
Expand Down
12 changes: 8 additions & 4 deletions pandas/tests/reshape/merge/test_merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,14 +757,18 @@ def test_other_datetime_unit(self, unit):
def test_other_timedelta_unit(self, unit):
# GH 13389
df1 = DataFrame({"entity_id": [101, 102]})
s = Series([None, None], index=[101, 102], name="days")
ser = Series([None, None], index=[101, 102], name="days")

dtype = f"m8[{unit}]"
df2 = s.astype(dtype).to_frame("days")
if unit in ["D", "h", "m"]:
# We get nearest supported unit, i.e. "s"
assert df2["days"].dtype == "m8[s]"
# We cannot astype, instead do nearest supported unit, i.e. "s"
msg = "Supported resolutions are 's', 'ms', 'us', 'ns'"
with pytest.raises(ValueError, match=msg):
ser.astype(dtype)

df2 = ser.astype("m8[s]").to_frame("days")
else:
df2 = ser.astype(dtype).to_frame("days")
assert df2["days"].dtype == dtype

result = df1.merge(df2, left_on="entity_id", right_index=True)
Expand Down