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,13 +207,52 @@ 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
208
249
@cache_readonly
209
250
@doc (Index ._should_fallback_to_positional )
210
251
def _should_fallback_to_positional (self ) -> bool :
211
- return False
252
+ if self .inferred_type == "floating" :
253
+ return False
254
+
255
+ return super ()._should_fallback_to_positional ()
212
256
213
257
@doc (Index ._convert_slice_indexer )
214
258
def _convert_slice_indexer (self , key : slice , kind : str ):
@@ -229,6 +273,21 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
229
273
# we will try to coerce to integers
230
274
return self ._maybe_cast_indexer (label )
231
275
276
+ @doc (Index ._convert_arr_indexer )
277
+ def _convert_arr_indexer (self , keyarr ) -> np .ndarray :
278
+ if is_unsigned_integer_dtype (self .dtype ):
279
+ # Cast the indexer to uint64 if possible so that the values returned
280
+ # from indexing are also uint64.
281
+ dtype = None
282
+ if is_integer_dtype (keyarr ) or (
283
+ lib .infer_dtype (keyarr , skipna = False ) == "integer"
284
+ ):
285
+ dtype = np .dtype (np .uint64 )
286
+
287
+ return com .asarray_tuplesafe (keyarr , dtype = dtype )
288
+
289
+ return super ()._convert_arr_indexer (keyarr )
290
+
232
291
# ----------------------------------------------------------------
233
292
234
293
@doc (Index ._shallow_copy )
@@ -260,13 +319,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
260
319
return is_numeric_dtype (dtype )
261
320
262
321
@classmethod
263
- def _assert_safe_casting (cls , data , subarr ):
322
+ def _assert_safe_casting (cls , data , subarr ) -> None :
264
323
"""
265
- Subclasses need to override this only if the process of casting data
266
- from some accepted dtype to the internal dtype(s) bears the risk of
267
- truncation (e.g. float to int).
324
+ Ensure incoming data can be represented with matching signed-ness.
268
325
"""
269
- pass
326
+ if is_integer_dtype (subarr .dtype ):
327
+ if not np .array_equal (data , subarr ):
328
+ raise TypeError ("Unsafe NumPy casting, you must explicitly cast" )
270
329
271
330
@property
272
331
def _is_all_dates (self ) -> bool :
0 commit comments