diff --git a/pandas/core/arrays/numpy_.py b/pandas/core/arrays/numpy_.py index 2ed42d699e862..216dbede39a6a 100644 --- a/pandas/core/arrays/numpy_.py +++ b/pandas/core/arrays/numpy_.py @@ -16,7 +16,12 @@ ) from pandas.compat.numpy import function as nv +from pandas.core.dtypes.astype import astype_array from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike +from pandas.core.dtypes.common import ( + is_dtype_equal, + pandas_dtype, +) from pandas.core.dtypes.dtypes import PandasDtype from pandas.core.dtypes.missing import isna @@ -185,6 +190,17 @@ def __array_ufunc__(self, ufunc: np.ufunc, method: str, *inputs, **kwargs): # ------------------------------------------------------------------------ # Pandas ExtensionArray Interface + def astype(self, dtype, copy: bool = True): + dtype = pandas_dtype(dtype) + + if is_dtype_equal(dtype, self.dtype): + if copy: + return self.copy() + return self + + result = astype_array(self._ndarray, dtype=dtype, copy=copy) + return result + def isna(self) -> np.ndarray: return isna(self._ndarray) diff --git a/pandas/core/arrays/string_.py b/pandas/core/arrays/string_.py index 9b26db07fc28f..ff32fd5697e07 100644 --- a/pandas/core/arrays/string_.py +++ b/pandas/core/arrays/string_.py @@ -454,7 +454,8 @@ def astype(self, dtype, copy: bool = True): values = arr.astype(dtype.numpy_dtype) return FloatingArray(values, mask, copy=False) elif isinstance(dtype, ExtensionDtype): - return super().astype(dtype, copy=copy) + # Skip the PandasArray.astype method + return ExtensionArray.astype(self, dtype, copy) elif np.issubdtype(dtype, np.floating): arr = self._ndarray.copy() mask = self.isna() diff --git a/pandas/tests/extension/test_numpy.py b/pandas/tests/extension/test_numpy.py index 4dd541ef3f0aa..9cf7a08357720 100644 --- a/pandas/tests/extension/test_numpy.py +++ b/pandas/tests/extension/test_numpy.py @@ -190,10 +190,7 @@ def assert_series_equal(cls, left, right, *args, **kwargs): class TestCasting(BaseNumPyTests, base.BaseCastingTests): - @skip_nested - def test_astype_str(self, data): - # ValueError: setting an array element with a sequence - super().test_astype_str(data) + pass class TestConstructors(BaseNumPyTests, base.BaseConstructorsTests): diff --git a/pandas/tests/series/methods/test_astype.py b/pandas/tests/series/methods/test_astype.py index 024cdf9300157..ce17614e1f8b7 100644 --- a/pandas/tests/series/methods/test_astype.py +++ b/pandas/tests/series/methods/test_astype.py @@ -352,14 +352,25 @@ def test_astype_cast_object_int_fail(self, dtype): def test_astype_float_to_uint_negatives_raise( self, float_numpy_dtype, any_unsigned_int_numpy_dtype ): - # GH#45151 - # TODO: same for EA float/uint dtypes + # GH#45151 We don't cast negative numbers to nonsense values + # TODO: same for EA float/uint dtypes, signed integers? arr = np.arange(5).astype(float_numpy_dtype) - 3 # includes negatives ser = Series(arr) - with pytest.raises(ValueError, match="losslessly"): + msg = "Cannot losslessly cast from .* to .*" + with pytest.raises(ValueError, match=msg): ser.astype(any_unsigned_int_numpy_dtype) + with pytest.raises(ValueError, match=msg): + ser.to_frame().astype(any_unsigned_int_numpy_dtype) + + with pytest.raises(ValueError, match=msg): + # We currently catch and re-raise in Index.astype + Index(ser).astype(any_unsigned_int_numpy_dtype) + + with pytest.raises(ValueError, match=msg): + ser.array.astype(any_unsigned_int_numpy_dtype) + def test_astype_cast_object_int(self): arr = Series(["1", "2", "3", "4"], dtype=object) result = arr.astype(int)