From c903ab377c1b5f92079b89042cc57cac59127c28 Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 15 Nov 2022 14:50:02 -0800 Subject: [PATCH 1/3] API: dtlike.astype(inty) --- doc/source/whatsnew/v2.0.0.rst | 1 + pandas/core/arrays/datetimelike.py | 38 ++----------------- pandas/core/dtypes/astype.py | 10 ----- pandas/tests/arrays/period/test_astype.py | 27 ++++--------- pandas/tests/arrays/test_datetimes.py | 19 +++------- pandas/tests/arrays/test_timedeltas.py | 19 +++------- .../indexes/datetimes/methods/test_astype.py | 18 +++------ pandas/tests/indexes/interval/test_astype.py | 21 +++++----- .../indexes/period/methods/test_astype.py | 15 +++----- .../indexes/timedeltas/methods/test_astype.py | 17 +++------ pandas/tests/series/test_constructors.py | 4 +- 11 files changed, 53 insertions(+), 136 deletions(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 032bcf09244e5..864fd89e2bd52 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -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:`??`) - 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`) - Passing a ``np.datetime64`` object with non-nanosecond resolution to :class:`Timestamp` will retain the input resolution if it is "s", "ms", or "ns"; otherwise it will be cast to the closest supported resolution (:issue:`49008`) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 1ba82fc2f063a..8e5aebfafe3b7 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -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 ( @@ -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: diff --git a/pandas/core/dtypes/astype.py b/pandas/core/dtypes/astype.py index 4dd49ec6b64bb..d191581a2bc7e 100644 --- a/pandas/core/dtypes/astype.py +++ b/pandas/core/dtypes/astype.py @@ -201,16 +201,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_datetime64tz_dtype(dtype) and is_datetime64_dtype(values.dtype): # Series.astype behavior pre-2.0 did # values.tz_localize("UTC").tz_convert(dtype.tz) diff --git a/pandas/tests/arrays/period/test_astype.py b/pandas/tests/arrays/period/test_astype.py index e9245c9ca786b..4a03754feac1b 100644 --- a/pandas/tests/arrays/period/test_astype.py +++ b/pandas/tests/arrays/period/test_astype.py @@ -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) diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 166362a9a8c30..660fe2bb9bd6a 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -382,20 +382,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): diff --git a/pandas/tests/arrays/test_timedeltas.py b/pandas/tests/arrays/test_timedeltas.py index 2fd7ccc9cf338..ecdcf5c04d53c 100644 --- a/pandas/tests/arrays/test_timedeltas.py +++ b/pandas/tests/arrays/test_timedeltas.py @@ -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): diff --git a/pandas/tests/indexes/datetimes/methods/test_astype.py b/pandas/tests/indexes/datetimes/methods/test_astype.py index ccbfd9217373b..9cb331ad7f18f 100644 --- a/pandas/tests/indexes/datetimes/methods/test_astype.py +++ b/pandas/tests/indexes/datetimes/methods/test_astype.py @@ -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: @@ -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): diff --git a/pandas/tests/indexes/interval/test_astype.py b/pandas/tests/indexes/interval/test_astype.py index c253a745ef5a2..59c555b9644a1 100644 --- a/pandas/tests/indexes/interval/test_astype.py +++ b/pandas/tests/indexes/interval/test_astype.py @@ -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) diff --git a/pandas/tests/indexes/period/methods/test_astype.py b/pandas/tests/indexes/period/methods/test_astype.py index 9720b751b87ce..993e48f2ebaf6 100644 --- a/pandas/tests/indexes/period/methods/test_astype.py +++ b/pandas/tests/indexes/period/methods/test_astype.py @@ -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: @@ -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") diff --git a/pandas/tests/indexes/timedeltas/methods/test_astype.py b/pandas/tests/indexes/timedeltas/methods/test_astype.py index 6302f8784e29b..90a7c8280851b 100644 --- a/pandas/tests/indexes/timedeltas/methods/test_astype.py +++ b/pandas/tests/indexes/timedeltas/methods/test_astype.py @@ -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: @@ -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 diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index 826ad20dfd54e..46281d62c0379 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -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") @@ -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") From f2ac3346769928692b42280eab3eefdd8a30b887 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 16 Nov 2022 09:52:13 -0800 Subject: [PATCH 2/3] Update doc/source/whatsnew/v2.0.0.rst Co-authored-by: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> --- doc/source/whatsnew/v2.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 864fd89e2bd52..3698710fd016c 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -329,7 +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:`??`) +- :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`) - 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`) - Passing a ``np.datetime64`` object with non-nanosecond resolution to :class:`Timestamp` will retain the input resolution if it is "s", "ms", or "ns"; otherwise it will be cast to the closest supported resolution (:issue:`49008`) From 87127aa8873e677efaa16082274ba199032aff6b Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 18 Nov 2022 11:44:56 -0800 Subject: [PATCH 3/3] int->int64 --- pandas/tests/indexes/datetimes/methods/test_astype.py | 2 +- pandas/tests/indexes/timedeltas/methods/test_astype.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexes/datetimes/methods/test_astype.py b/pandas/tests/indexes/datetimes/methods/test_astype.py index 9cb331ad7f18f..0e59f162cd03d 100644 --- a/pandas/tests/indexes/datetimes/methods/test_astype.py +++ b/pandas/tests/indexes/datetimes/methods/test_astype.py @@ -29,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, diff --git a/pandas/tests/indexes/timedeltas/methods/test_astype.py b/pandas/tests/indexes/timedeltas/methods/test_astype.py index 90a7c8280851b..c728d636fb5db 100644 --- a/pandas/tests/indexes/timedeltas/methods/test_astype.py +++ b/pandas/tests/indexes/timedeltas/methods/test_astype.py @@ -54,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" )