Skip to content

REF: share NumericIndex.astype+Index.astype #45405

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 5 commits into from
Jan 22, 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
30 changes: 29 additions & 1 deletion pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from pandas.compat.numpy import function as nv
from pandas.errors import (
DuplicateLabelError,
IntCastingNaNError,
InvalidIndexError,
)
from pandas.util._decorators import (
Expand All @@ -65,6 +66,7 @@
rewrite_exception,
)

from pandas.core.dtypes.astype import astype_nansafe
from pandas.core.dtypes.cast import (
can_hold_element,
common_dtype_categorical_compat,
Expand Down Expand Up @@ -1051,6 +1053,14 @@ def astype(self, dtype, copy: bool = True):
with rewrite_exception(type(values).__name__, type(self).__name__):
new_values = values.astype(dtype, copy=copy)

elif is_float_dtype(self.dtype) and needs_i8_conversion(dtype):
# NB: this must come before the ExtensionDtype check below
# TODO: this differs from Series behavior; can/should we align them?
raise TypeError(
f"Cannot convert Float64Index to dtype {dtype}; integer "
"values are required for conversion"
)

elif isinstance(dtype, ExtensionDtype):
cls = dtype.construct_array_type()
# Note: for RangeIndex and CategoricalDtype self vs self._values
Expand All @@ -1059,13 +1069,31 @@ def astype(self, dtype, copy: bool = True):

else:
try:
new_values = values.astype(dtype, copy=copy)
if dtype == str:
# GH#38607
new_values = values.astype(dtype, copy=copy)
else:
# GH#13149 specifically use astype_nansafe instead of astype
new_values = astype_nansafe(values, dtype=dtype, copy=copy)
except IntCastingNaNError:
raise
except (TypeError, ValueError) as err:
if dtype.kind == "u" and "losslessly" in str(err):
# keep the message from _astype_float_to_int_nansafe
raise
raise TypeError(
f"Cannot cast {type(self).__name__} to dtype {dtype}"
) from err

# pass copy=False because any copying will be done in the astype above
if self._is_backward_compat_public_numeric_index:
# this block is needed so e.g. NumericIndex[int8].astype("int32") returns
# NumericIndex[int32] and not Int64Index with dtype int64.
# When Int64Index etc. are removed from the code base, removed this also.
if isinstance(dtype, np.dtype) and is_numeric_dtype(dtype):
return self._constructor(
new_values, name=self.name, dtype=dtype, copy=False
)
return Index(new_values, name=self.name, dtype=new_values.dtype, copy=False)

_index_shared_docs[
Expand Down
32 changes: 0 additions & 32 deletions pandas/core/indexes/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,15 @@
)
from pandas.util._exceptions import find_stack_level

from pandas.core.dtypes.astype import astype_nansafe
from pandas.core.dtypes.common import (
is_dtype_equal,
is_extension_array_dtype,
is_float,
is_float_dtype,
is_integer_dtype,
is_numeric_dtype,
is_scalar,
is_signed_integer_dtype,
is_unsigned_integer_dtype,
needs_i8_conversion,
pandas_dtype,
)
from pandas.core.dtypes.generic import ABCSeries
Expand Down Expand Up @@ -232,35 +229,6 @@ def __contains__(self, key) -> bool:
except (OverflowError, TypeError, ValueError):
return False

@doc(Index.astype)
def astype(self, dtype, copy: bool = True):
dtype = pandas_dtype(dtype)
if is_float_dtype(self.dtype):
if needs_i8_conversion(dtype):
raise TypeError(
f"Cannot convert Float64Index to dtype {dtype}; integer "
"values are required for conversion"
)
elif is_integer_dtype(dtype) and not is_extension_array_dtype(dtype):
# TODO(ExtensionIndex); this can change once we have an EA Index type
# GH 13149
arr = astype_nansafe(self._values, dtype=dtype)
if isinstance(self, Float64Index):
if dtype.kind == "i":
return Int64Index(arr, name=self.name)
else:
return UInt64Index(arr, name=self.name)
else:
return NumericIndex(arr, name=self.name, dtype=dtype)
elif self._is_backward_compat_public_numeric_index:
# this block is needed so e.g. NumericIndex[int8].astype("int32") returns
# NumericIndex[int32] and not Int64Index with dtype int64.
# When Int64Index etc. are removed from the code base, removed this also.
if not is_extension_array_dtype(dtype) and is_numeric_dtype(dtype):
return self._constructor(self, dtype=dtype, copy=copy)

return super().astype(dtype, copy=copy)

# ----------------------------------------------------------------
# Indexing Methods

Expand Down