Skip to content

Commit

Permalink
API: dtlike.astype(inty) (#49715)
Browse files Browse the repository at this point in the history
* API: dtlike.astype(inty)

* Update doc/source/whatsnew/v2.0.0.rst

Co-authored-by: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com>

* int->int64

Co-authored-by: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com>
  • Loading branch information
jbrockmendel and mroeschke authored Jan 11, 2023
1 parent 710b83b commit 279138c
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 138 deletions.
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 @@ -515,6 +515,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 @@ -96,7 +96,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 @@ -468,39 +467,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 @@ -171,16 +171,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 @@ -377,20 +377,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_astype_to_sparse_dt64(self):
Expand Down
21 changes: 7 additions & 14 deletions pandas/tests/arrays/test_timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,20 +190,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)

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)

def test_setitem_clears_freq(self):
Expand Down
18 changes: 5 additions & 13 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 Down Expand Up @@ -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 @@ -974,7 +974,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 @@ -1508,7 +1508,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

0 comments on commit 279138c

Please sign in to comment.