8080
8181
8282class 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
8890
89- _default_dtype : np . dtype | None = None
91+ _typ = "numericindex"
9092 _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+ )
9398 _is_numeric_dtype = True
9499 _can_hold_strings = False
95100
@@ -207,13 +212,52 @@ def _ensure_dtype(
207212 else :
208213 return dtype
209214
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+
210251 # ----------------------------------------------------------------
211252 # Indexing Methods
212253
213254 @cache_readonly
214255 @doc (Index ._should_fallback_to_positional )
215256 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 ()
217261
218262 @doc (Index ._convert_slice_indexer )
219263 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):
234278 # we will try to coerce to integers
235279 return self ._maybe_cast_indexer (label )
236280
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+
237296 # ----------------------------------------------------------------
238297
239298 @doc (Index ._shallow_copy )
@@ -265,13 +324,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
265324 return is_numeric_dtype (dtype )
266325
267326 @classmethod
268- def _assert_safe_casting (cls , data , subarr ):
327+ def _assert_safe_casting (cls , data , subarr ) -> None :
269328 """
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.
273330 """
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" )
275334
276335 @property
277336 def _is_all_dates (self ) -> bool :
0 commit comments