80
80
81
81
82
82
class NumericIndex (Index ):
83
- """
84
- Provide numeric type operations.
85
-
86
- This is an abstract class.
87
- """
83
+ _numeric_index_descr_args = {
84
+ "klass" : "NumericIndex" ,
85
+ "ltype" : "integer or float" ,
86
+ "dtype" : "inferred" ,
87
+ "extra" : "" ,
88
+ }
89
+ __doc__ = _num_index_shared_docs ["class_descr" ] % _numeric_index_descr_args
88
90
89
- _default_dtype : np . dtype | None = None
91
+ _typ = "numericindex"
90
92
_values : np .ndarray
91
- _dtype_validation_metadata : tuple [Callable [..., bool ], str ]
92
-
93
+ _default_dtype : np .dtype | None = None
94
+ _dtype_validation_metadata : tuple [Callable [..., bool ], str ] = (
95
+ is_numeric_dtype ,
96
+ "numeric type" ,
97
+ )
93
98
_is_numeric_dtype = True
94
99
_can_hold_strings = False
95
100
@@ -207,13 +212,52 @@ def _ensure_dtype(
207
212
else :
208
213
return dtype
209
214
215
+ def __contains__ (self , key ) -> bool :
216
+ """
217
+ Check if key is a float and has a decimal. If it has, return False.
218
+ """
219
+ if not is_integer_dtype (self .dtype ):
220
+ return super ().__contains__ (key )
221
+
222
+ hash (key )
223
+ try :
224
+ if is_float (key ) and int (key ) != key :
225
+ # otherwise the `key in self._engine` check casts e.g. 1.1 -> 1
226
+ return False
227
+ return key in self ._engine
228
+ except (OverflowError , TypeError , ValueError ):
229
+ return False
230
+
231
+ @doc (Index .astype )
232
+ def astype (self , dtype , copy = True ):
233
+ if is_float_dtype (self .dtype ):
234
+ dtype = pandas_dtype (dtype )
235
+ if needs_i8_conversion (dtype ):
236
+ raise TypeError (
237
+ f"Cannot convert Float64Index to dtype { dtype } ; integer "
238
+ "values are required for conversion"
239
+ )
240
+ elif is_integer_dtype (dtype ) and not is_extension_array_dtype (dtype ):
241
+ # TODO(jreback); this can change once we have an EA Index type
242
+ # GH 13149
243
+ arr = astype_nansafe (self ._values , dtype = dtype )
244
+ if isinstance (self , Float64Index ):
245
+ return Int64Index (arr , name = self .name )
246
+ else :
247
+ return NumericIndex (arr , name = self .name , dtype = dtype )
248
+
249
+ return super ().astype (dtype , copy = copy )
250
+
210
251
# ----------------------------------------------------------------
211
252
# Indexing Methods
212
253
213
254
@cache_readonly
214
255
@doc (Index ._should_fallback_to_positional )
215
256
def _should_fallback_to_positional (self ) -> bool :
216
- return False
257
+ if self .inferred_type == "floating" :
258
+ return False
259
+
260
+ return super ()._should_fallback_to_positional ()
217
261
218
262
@doc (Index ._convert_slice_indexer )
219
263
def _convert_slice_indexer (self , key : slice , kind : str ):
@@ -234,6 +278,21 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
234
278
# we will try to coerce to integers
235
279
return self ._maybe_cast_indexer (label )
236
280
281
+ @doc (Index ._convert_arr_indexer )
282
+ def _convert_arr_indexer (self , keyarr ) -> np .ndarray :
283
+ if is_unsigned_integer_dtype (self .dtype ):
284
+ # Cast the indexer to uint64 if possible so that the values returned
285
+ # from indexing are also uint64.
286
+ dtype = None
287
+ if is_integer_dtype (keyarr ) or (
288
+ lib .infer_dtype (keyarr , skipna = False ) == "integer"
289
+ ):
290
+ dtype = np .dtype (np .uint64 )
291
+
292
+ return com .asarray_tuplesafe (keyarr , dtype = dtype )
293
+
294
+ return super ()._convert_arr_indexer (keyarr )
295
+
237
296
# ----------------------------------------------------------------
238
297
239
298
@doc (Index ._shallow_copy )
@@ -265,13 +324,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
265
324
return is_numeric_dtype (dtype )
266
325
267
326
@classmethod
268
- def _assert_safe_casting (cls , data , subarr ):
327
+ def _assert_safe_casting (cls , data , subarr ) -> None :
269
328
"""
270
- Subclasses need to override this only if the process of casting data
271
- from some accepted dtype to the internal dtype(s) bears the risk of
272
- truncation (e.g. float to int).
329
+ Ensure incoming data can be represented with matching signed-ness.
273
330
"""
274
- pass
331
+ if is_integer_dtype (subarr .dtype ):
332
+ if not np .array_equal (data , subarr ):
333
+ raise TypeError ("Unsafe NumPy casting, you must explicitly cast" )
275
334
276
335
@property
277
336
def _is_all_dates (self ) -> bool :
0 commit comments