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
@@ -202,9 +207,63 @@ def _ensure_dtype(
202
207
else :
203
208
return dtype
204
209
210
+ def __contains__ (self , key ) -> bool :
211
+ """
212
+ Check if key is a float and has a decimal. If it has, return False.
213
+ """
214
+ if not is_integer_dtype (self .dtype ):
215
+ return super ().__contains__ (key )
216
+
217
+ hash (key )
218
+ try :
219
+ if is_float (key ) and int (key ) != key :
220
+ # otherwise the `key in self._engine` check casts e.g. 1.1 -> 1
221
+ return False
222
+ return key in self ._engine
223
+ except (OverflowError , TypeError , ValueError ):
224
+ return False
225
+
226
+ @doc (Index .astype )
227
+ def astype (self , dtype , copy = True ):
228
+ if is_float_dtype (self .dtype ):
229
+ dtype = pandas_dtype (dtype )
230
+ if needs_i8_conversion (dtype ):
231
+ raise TypeError (
232
+ f"Cannot convert Float64Index to dtype { dtype } ; integer "
233
+ "values are required for conversion"
234
+ )
235
+ elif is_integer_dtype (dtype ) and not is_extension_array_dtype (dtype ):
236
+ # TODO(jreback); this can change once we have an EA Index type
237
+ # GH 13149
238
+ arr = astype_nansafe (self ._values , dtype = dtype )
239
+ if isinstance (self , Float64Index ):
240
+ return Int64Index (arr , name = self .name )
241
+ else :
242
+ return NumericIndex (arr , name = self .name , dtype = dtype )
243
+
244
+ return super ().astype (dtype , copy = copy )
245
+
205
246
# ----------------------------------------------------------------
206
247
# Indexing Methods
207
248
249
+ @doc (Index ._should_fallback_to_positional )
250
+ def _should_fallback_to_positional (self ) -> bool :
251
+ if self .inferred_type == "floating" :
252
+ return False
253
+
254
+ return super ()._should_fallback_to_positional ()
255
+
256
+ @doc (Index ._convert_slice_indexer )
257
+ def _convert_slice_indexer (self , key : slice , kind : str ):
258
+ if is_float_dtype (self .dtype ):
259
+ assert kind in ["loc" , "getitem" ]
260
+
261
+ # We always treat __getitem__ slicing as label-based
262
+ # translate to locations
263
+ return self .slice_indexer (key .start , key .stop , key .step , kind = kind )
264
+
265
+ return super ()._convert_slice_indexer (key , kind = kind )
266
+
208
267
@doc (Index ._maybe_cast_slice_bound )
209
268
def _maybe_cast_slice_bound (self , label , side : str , kind = lib .no_default ):
210
269
assert kind in ["loc" , "getitem" , None , lib .no_default ]
@@ -213,6 +272,21 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
213
272
# we will try to coerce to integers
214
273
return self ._maybe_cast_indexer (label )
215
274
275
+ @doc (Index ._convert_arr_indexer )
276
+ def _convert_arr_indexer (self , keyarr ) -> np .ndarray :
277
+ if is_unsigned_integer_dtype (self .dtype ):
278
+ # Cast the indexer to uint64 if possible so that the values returned
279
+ # from indexing are also uint64.
280
+ dtype = None
281
+ if is_integer_dtype (keyarr ) or (
282
+ lib .infer_dtype (keyarr , skipna = False ) == "integer"
283
+ ):
284
+ dtype = np .dtype (np .uint64 )
285
+
286
+ return com .asarray_tuplesafe (keyarr , dtype = dtype )
287
+
288
+ return super ()._convert_arr_indexer (keyarr )
289
+
216
290
# ----------------------------------------------------------------
217
291
218
292
@doc (Index ._shallow_copy )
@@ -244,13 +318,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
244
318
return is_numeric_dtype (dtype )
245
319
246
320
@classmethod
247
- def _assert_safe_casting (cls , data , subarr ):
321
+ def _assert_safe_casting (cls , data , subarr ) -> None :
248
322
"""
249
- Subclasses need to override this only if the process of casting data
250
- from some accepted dtype to the internal dtype(s) bears the risk of
251
- truncation (e.g. float to int).
323
+ Ensure incoming data can be represented with matching signed-ness.
252
324
"""
253
- pass
325
+ if is_integer_dtype (subarr .dtype ):
326
+ if not np .array_equal (data , subarr ):
327
+ raise TypeError ("Unsafe NumPy casting, you must explicitly cast" )
254
328
255
329
@property
256
330
def _is_all_dates (self ) -> bool :
0 commit comments