Skip to content

API: dtlike.astype(inty) #49715

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 6 commits into from
Jan 11, 2023
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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v2.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ Other API changes
- Default value of ``dtype`` in :func:`get_dummies` is changed to ``bool`` from ``uint8`` (:issue:`45848`)
- :meth:`DataFrame.astype`, :meth:`Series.astype`, and :meth:`DatetimeIndex.astype` casting datetime64 data to any of "datetime64[s]", "datetime64[ms]", "datetime64[us]" will return an object with the given resolution instead of coercing back to "datetime64[ns]" (:issue:`48928`)
- :meth:`DataFrame.astype`, :meth:`Series.astype`, and :meth:`DatetimeIndex.astype` casting timedelta64 data to any of "timedelta64[s]", "timedelta64[ms]", "timedelta64[us]" will return an object with the given resolution instead of coercing to "float64" dtype (:issue:`48963`)
- :meth:`DatetimeIndex.astype`, :meth:`TimedeltaIndex.astype`, :meth:`PeriodIndex.astype` :meth:`Series.astype`, :meth:`DataFrame.astype` with ``datetime64``, ``timedelta64`` or :class:`PeriodDtype` dtypes no longer allow converting to integer dtypes other than "int64", do ``obj.astype('int64', copy=False).astype(dtype)`` instead (:issue:`49715`)
- :meth:`Index.astype` now allows casting from ``float64`` dtype to datetime-like dtypes, matching :class:`Series` behavior (:issue:`49660`)
- Passing data with dtype of "timedelta64[s]", "timedelta64[ms]", or "timedelta64[us]" to :class:`TimedeltaIndex`, :class:`Series`, or :class:`DataFrame` constructors will now retain that dtype instead of casting to "timedelta64[ns]"; timedelta64 data with lower resolution will be cast to the lowest supported resolution "timedelta64[s]" (:issue:`49014`)
- Passing ``dtype`` of "timedelta64[s]", "timedelta64[ms]", or "timedelta64[us]" to :class:`TimedeltaIndex`, :class:`Series`, or :class:`DataFrame` constructors will now retain that dtype instead of casting to "timedelta64[ns]"; passing a dtype with lower resolution for :class:`Series` or :class:`DataFrame` will be cast to the lowest supported resolution "timedelta64[s]" (:issue:`49014`)
Expand Down
38 changes: 4 additions & 34 deletions pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@
is_period_dtype,
is_string_dtype,
is_timedelta64_dtype,
is_unsigned_integer_dtype,
pandas_dtype,
)
from pandas.core.dtypes.dtypes import (
Expand Down Expand Up @@ -449,39 +448,10 @@ def astype(self, dtype, copy: bool = True):
# we deliberately ignore int32 vs. int64 here.
# See https://github.com/pandas-dev/pandas/issues/24381 for more.
values = self.asi8

if is_unsigned_integer_dtype(dtype):
# Again, we ignore int32 vs. int64
values = values.view("uint64")
if dtype != np.uint64:
# GH#45034
warnings.warn(
f"The behavior of .astype from {self.dtype} to {dtype} is "
"deprecated. In a future version, this astype will return "
"exactly the specified dtype instead of uint64, and will "
"raise if that conversion overflows.",
FutureWarning,
stacklevel=find_stack_level(),
)
elif (self.asi8 < 0).any():
# GH#45034
warnings.warn(
f"The behavior of .astype from {self.dtype} to {dtype} is "
"deprecated. In a future version, this astype will "
"raise if the conversion overflows, as it did in this "
"case with negative int64 values.",
FutureWarning,
stacklevel=find_stack_level(),
)
elif dtype != np.int64:
# GH#45034
warnings.warn(
f"The behavior of .astype from {self.dtype} to {dtype} is "
"deprecated. In a future version, this astype will return "
"exactly the specified dtype instead of int64, and will "
"raise if that conversion overflows.",
FutureWarning,
stacklevel=find_stack_level(),
if dtype != np.int64:
raise TypeError(
f"Converting from {self.dtype} to {dtype} is not supported. "
"Do obj.astype('int64').astype(dtype) instead"
)

if copy:
Expand Down
10 changes: 0 additions & 10 deletions pandas/core/dtypes/astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,16 +200,6 @@ def astype_array(values: ArrayLike, dtype: DtypeObj, copy: bool = False) -> Arra
-------
ndarray or ExtensionArray
"""
if (
values.dtype.kind in ["m", "M"]
and dtype.kind in ["i", "u"]
and isinstance(dtype, np.dtype)
and dtype.itemsize != 8
):
# TODO(2.0) remove special case once deprecation on DTA/TDA is enforced
msg = rf"cannot astype a datetimelike from [{values.dtype}] to [{dtype}]"
raise TypeError(msg)

if is_dtype_equal(values.dtype, dtype):
if copy:
return values.copy()
Expand Down
27 changes: 7 additions & 20 deletions pandas/tests/arrays/period/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,13 @@ def test_astype_int(dtype):
# Period/Datetime/Timedelta astype
arr = period_array(["2000", "2001", None], freq="D")

if np.dtype(dtype).kind == "u":
expected_dtype = np.dtype("uint64")
warn1 = FutureWarning
else:
expected_dtype = np.dtype("int64")
warn1 = None

msg_overflow = "will raise if the conversion overflows"
with tm.assert_produces_warning(warn1, match=msg_overflow):
expected = arr.astype(expected_dtype)

warn = None if dtype == expected_dtype else FutureWarning
msg = " will return exactly the specified dtype"
if warn is None and warn1 is not None:
warn = warn1
msg = msg_overflow
with tm.assert_produces_warning(warn, match=msg):
result = arr.astype(dtype)

assert result.dtype == expected_dtype
if np.dtype(dtype) != np.int64:
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype(dtype)
return

result = arr.astype(dtype)
expected = arr._ndarray.view("i8")
tm.assert_numpy_array_equal(result, expected)


Expand Down
19 changes: 6 additions & 13 deletions pandas/tests/arrays/test_datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,20 +387,13 @@ def test_astype_copies(self, dtype, other):
def test_astype_int(self, dtype):
arr = DatetimeArray._from_sequence([pd.Timestamp("2000"), pd.Timestamp("2001")])

if np.dtype(dtype).kind == "u":
expected_dtype = np.dtype("uint64")
else:
expected_dtype = np.dtype("int64")
expected = arr.astype(expected_dtype)

warn = None
if dtype != expected_dtype:
warn = FutureWarning
msg = " will return exactly the specified dtype"
with tm.assert_produces_warning(warn, match=msg):
result = arr.astype(dtype)
if np.dtype(dtype) != np.int64:
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype(dtype)
return

assert result.dtype == expected_dtype
result = arr.astype(dtype)
expected = arr._ndarray.view("i8")
tm.assert_numpy_array_equal(result, expected)

def test_tz_setter_raises(self):
Expand Down
19 changes: 6 additions & 13 deletions pandas/tests/arrays/test_timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,20 +194,13 @@ class TestTimedeltaArray:
def test_astype_int(self, dtype):
arr = TimedeltaArray._from_sequence([Timedelta("1H"), Timedelta("2H")])

if np.dtype(dtype).kind == "u":
expected_dtype = np.dtype("uint64")
else:
expected_dtype = np.dtype("int64")
expected = arr.astype(expected_dtype)

warn = None
if dtype != expected_dtype:
warn = FutureWarning
msg = " will return exactly the specified dtype"
with tm.assert_produces_warning(warn, match=msg):
result = arr.astype(dtype)
if np.dtype(dtype) != np.int64:
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype(dtype)
return

assert result.dtype == expected_dtype
result = arr.astype(dtype)
expected = arr._ndarray.view("i8")
tm.assert_numpy_array_equal(result, expected)

def test_setitem_clears_freq(self):
Expand Down
20 changes: 6 additions & 14 deletions pandas/tests/indexes/datetimes/methods/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
date_range,
)
import pandas._testing as tm
from pandas.core.api import (
Int64Index,
UInt64Index,
)
from pandas.core.api import Int64Index


class TestDatetimeIndex:
Expand All @@ -32,7 +29,7 @@ def test_astype(self):
)
tm.assert_index_equal(result, expected)

result = idx.astype(int)
result = idx.astype(np.int64)
expected = Int64Index(
[1463356800000000000] + [-9223372036854775808] * 3,
dtype=np.int64,
Expand All @@ -47,16 +44,11 @@ def test_astype(self):

def test_astype_uint(self):
arr = date_range("2000", periods=2, name="idx")
expected = UInt64Index(
np.array([946684800000000000, 946771200000000000], dtype="uint64"),
name="idx",
)
tm.assert_index_equal(arr.astype("uint64"), expected)

msg = "will return exactly the specified dtype instead of uint64"
with tm.assert_produces_warning(FutureWarning, match=msg):
res = arr.astype("uint32")
tm.assert_index_equal(res, expected)
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint64")
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint32")

def test_astype_with_tz(self):

Expand Down
21 changes: 12 additions & 9 deletions pandas/tests/indexes/interval/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,18 @@ def index(self, request):
def test_subtype_integer(self, index, subtype):
dtype = IntervalDtype(subtype, "right")

warn = None
if index.isna().any() and subtype == "uint64":
warn = FutureWarning
msg = "In a future version, this astype will raise if the conversion overflows"

with tm.assert_produces_warning(warn, match=msg):
result = index.astype(dtype)
new_left = index.left.astype(subtype)
new_right = index.right.astype(subtype)
if subtype != "int64":
msg = (
r"Cannot convert interval\[(timedelta64|datetime64)\[ns.*\], .*\] "
r"to interval\[uint64, .*\]"
)
with pytest.raises(TypeError, match=msg):
index.astype(dtype)
return

result = index.astype(dtype)
new_left = index.left.astype(subtype)
new_right = index.right.astype(subtype)

expected = IntervalIndex.from_arrays(new_left, new_right, closed=index.closed)
tm.assert_index_equal(result, expected)
Expand Down
15 changes: 5 additions & 10 deletions pandas/tests/indexes/period/methods/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@
period_range,
)
import pandas._testing as tm
from pandas.core.indexes.api import (
Int64Index,
UInt64Index,
)
from pandas.core.indexes.api import Int64Index


class TestPeriodIndexAsType:
Expand Down Expand Up @@ -55,13 +52,11 @@ def test_astype_conversion(self):

def test_astype_uint(self):
arr = period_range("2000", periods=2, name="idx")
expected = UInt64Index(np.array([10957, 10958], dtype="uint64"), name="idx")
tm.assert_index_equal(arr.astype("uint64"), expected)

msg = "will return exactly the specified dtype instead of uint64"
with tm.assert_produces_warning(FutureWarning, match=msg):
res = arr.astype("uint32")
tm.assert_index_equal(res, expected)
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint64")
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint32")

def test_astype_object(self):
idx = PeriodIndex([], freq="M")
Expand Down
19 changes: 6 additions & 13 deletions pandas/tests/indexes/timedeltas/methods/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
timedelta_range,
)
import pandas._testing as tm
from pandas.core.api import (
Int64Index,
UInt64Index,
)
from pandas.core.api import Int64Index


class TestTimedeltaIndex:
Expand Down Expand Up @@ -57,7 +54,7 @@ def test_astype(self):
)
tm.assert_index_equal(result, expected)

result = idx.astype(int)
result = idx.astype(np.int64)
expected = Int64Index(
[100000000000000] + [-9223372036854775808] * 3, dtype=np.int64, name="idx"
)
Expand All @@ -74,15 +71,11 @@ def test_astype(self):

def test_astype_uint(self):
arr = timedelta_range("1H", periods=2)
expected = UInt64Index(
np.array([3600000000000, 90000000000000], dtype="uint64")
)
tm.assert_index_equal(arr.astype("uint64"), expected)

msg = "will return exactly the specified dtype instead of uint64"
with tm.assert_produces_warning(FutureWarning, match=msg):
res = arr.astype("uint32")
tm.assert_index_equal(res, expected)
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint64")
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint32")

def test_astype_timedelta64(self):
# GH 13149, GH 13209
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/series/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ def test_constructor_dtype_datetime64_11(self):
dts.astype("int64")

# invalid casting
msg = r"cannot astype a datetimelike from \[datetime64\[ns\]\] to \[int32\]"
msg = r"Converting from datetime64\[ns\] to int32 is not supported"
with pytest.raises(TypeError, match=msg):
dts.astype("int32")

Expand Down Expand Up @@ -1501,7 +1501,7 @@ def test_constructor_dtype_timedelta64(self):
td.astype("int64")

# invalid casting
msg = r"cannot astype a datetimelike from \[timedelta64\[ns\]\] to \[int32\]"
msg = r"Converting from timedelta64\[ns\] to int32 is not supported"
with pytest.raises(TypeError, match=msg):
td.astype("int32")

Expand Down