@@ -97,6 +97,7 @@ class NumericIndex(Index):
97
97
)
98
98
_is_numeric_dtype = True
99
99
_can_hold_strings = False
100
+ _is_backward_compat_public_numeric_index : bool = True
100
101
101
102
@cache_readonly
102
103
def _can_hold_na (self ) -> bool :
@@ -165,7 +166,15 @@ def _ensure_array(cls, data, dtype, copy: bool):
165
166
dtype = cls ._ensure_dtype (dtype )
166
167
167
168
if copy or not is_dtype_equal (data .dtype , dtype ):
168
- subarr = np .array (data , dtype = dtype , copy = copy )
169
+ # TODO: the try/except below is because it's difficult to predict the error
170
+ # and/or error message from different combinations of data and dtype.
171
+ # Efforts to avoid this try/except welcome.
172
+ # See https://github.com/pandas-dev/pandas/pull/41153#discussion_r676206222
173
+ try :
174
+ subarr = np .array (data , dtype = dtype , copy = copy )
175
+ cls ._validate_dtype (subarr .dtype )
176
+ except (TypeError , ValueError ):
177
+ raise ValueError (f"data is not compatible with { cls .__name__ } " )
169
178
cls ._assert_safe_casting (data , subarr )
170
179
else :
171
180
subarr = data
@@ -189,12 +198,24 @@ def _validate_dtype(cls, dtype: Dtype | None) -> None:
189
198
)
190
199
191
200
@classmethod
192
- def _ensure_dtype (
193
- cls ,
194
- dtype : Dtype | None ,
195
- ) -> np .dtype | None :
196
- """Ensure int64 dtype for Int64Index, etc. Assumed dtype is validated."""
197
- return cls ._default_dtype
201
+ def _ensure_dtype (cls , dtype : Dtype | None ) -> np .dtype | None :
202
+ """
203
+ Ensure int64 dtype for Int64Index etc. but allow int32 etc. for NumericIndex.
204
+
205
+ Assumes dtype has already been validated.
206
+ """
207
+ if dtype is None :
208
+ return cls ._default_dtype
209
+
210
+ dtype = pandas_dtype (dtype )
211
+ assert isinstance (dtype , np .dtype )
212
+
213
+ if cls ._is_backward_compat_public_numeric_index :
214
+ # dtype for NumericIndex
215
+ return dtype
216
+ else :
217
+ # dtype for Int64Index, UInt64Index etc. Needed for backwards compat.
218
+ return cls ._default_dtype
198
219
199
220
def __contains__ (self , key ) -> bool :
200
221
"""
@@ -214,8 +235,8 @@ def __contains__(self, key) -> bool:
214
235
215
236
@doc (Index .astype )
216
237
def astype (self , dtype , copy = True ):
238
+ dtype = pandas_dtype (dtype )
217
239
if is_float_dtype (self .dtype ):
218
- dtype = pandas_dtype (dtype )
219
240
if needs_i8_conversion (dtype ):
220
241
raise TypeError (
221
242
f"Cannot convert Float64Index to dtype { dtype } ; integer "
@@ -225,7 +246,16 @@ def astype(self, dtype, copy=True):
225
246
# TODO(jreback); this can change once we have an EA Index type
226
247
# GH 13149
227
248
arr = astype_nansafe (self ._values , dtype = dtype )
228
- return Int64Index (arr , name = self .name )
249
+ if isinstance (self , Float64Index ):
250
+ return Int64Index (arr , name = self .name )
251
+ else :
252
+ return NumericIndex (arr , name = self .name , dtype = dtype )
253
+ elif self ._is_backward_compat_public_numeric_index :
254
+ # this block is needed so e.g. NumericIndex[int8].astype("int32") returns
255
+ # NumericIndex[int32] and not Int64Index with dtype int64.
256
+ # When Int64Index etc. are removed from the code base, removed this also.
257
+ if not is_extension_array_dtype (dtype ) and is_numeric_dtype (dtype ):
258
+ return self ._constructor (self , dtype = dtype , copy = copy )
229
259
230
260
return super ().astype (dtype , copy = copy )
231
261
@@ -335,6 +365,8 @@ class IntegerIndex(NumericIndex):
335
365
This is an abstract class for Int64Index, UInt64Index.
336
366
"""
337
367
368
+ _is_backward_compat_public_numeric_index : bool = False
369
+
338
370
@property
339
371
def asi8 (self ) -> np .ndarray :
340
372
# do not cache or you'll create a memory leak
@@ -399,3 +431,4 @@ class Float64Index(NumericIndex):
399
431
_engine_type = libindex .Float64Engine
400
432
_default_dtype = np .dtype (np .float64 )
401
433
_dtype_validation_metadata = (is_float_dtype , "float" )
434
+ _is_backward_compat_public_numeric_index : bool = False
0 commit comments