Skip to content

Commit 81b014f

Browse files
committed
Make (Int|UInt|Float)64Index inherit from NumIndex
1 parent 5bc2568 commit 81b014f

File tree

4 files changed

+119
-197
lines changed

4 files changed

+119
-197
lines changed

pandas/core/indexes/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ def __new__(
442442
return Index._simple_new(data, name=name)
443443

444444
# index-like
445-
elif isinstance(data, NumIndex) and dtype is None:
445+
elif type(data) is NumIndex and dtype is None:
446446
return NumIndex(data, name=name, copy=copy)
447447
elif isinstance(data, (np.ndarray, Index, ABCSeries)):
448448

@@ -5490,8 +5490,8 @@ def map(self, mapper, na_action=None):
54905490
# empty
54915491
attributes["dtype"] = self.dtype
54925492

5493-
if isinstance(self, NumIndex):
5494-
return NumIndex(new_values, **attributes)
5493+
if type(self) is NumIndex:
5494+
return type(self)(new_values, **attributes)
54955495

54965496
return Index(new_values, **attributes)
54975497

pandas/core/indexes/category.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def astype(self, dtype, copy: bool = True) -> Index:
285285
dtype = pandas_dtype(dtype)
286286

287287
cat = self.categories
288-
if isinstance(cat, NumIndex):
288+
if type(cat) is NumIndex:
289289
try:
290290
cat._validate_dtype(dtype)
291291
except ValueError:

pandas/core/indexes/numeric.py

+114-192
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class NumericIndex(Index):
5555
"""
5656

5757
_values: np.ndarray
58-
_default_dtype: np.dtype
58+
_default_dtype: np.dtype | None = None
5959
_dtype_validation_metadata: tuple[Callable[..., bool], str]
6060

6161
_is_numeric_dtype = True
@@ -88,7 +88,10 @@ def _ensure_array(cls, data, dtype, copy: bool):
8888
if issubclass(data.dtype.type, str):
8989
cls._string_data_error(data)
9090

91-
if copy or not is_dtype_equal(data.dtype, cls._default_dtype):
91+
if copy or (
92+
cls._default_dtype is not None
93+
and not is_dtype_equal(data.dtype, cls._default_dtype)
94+
):
9295
subarr = np.array(data, dtype=cls._default_dtype, copy=copy)
9396
cls._assert_safe_casting(data, subarr)
9497
else:
@@ -202,173 +205,6 @@ def _is_all_dates(self) -> bool:
202205
An Index instance can **only** contain hashable objects.
203206
"""
204207

205-
_int64_descr_args = {
206-
"klass": "Int64Index",
207-
"ltype": "integer",
208-
"dtype": "int64",
209-
"extra": "",
210-
}
211-
212-
213-
class IntegerIndex(NumericIndex):
214-
"""
215-
This is an abstract class for Int64Index, UInt64Index.
216-
"""
217-
218-
_default_dtype: np.dtype
219-
_can_hold_na = False
220-
221-
@classmethod
222-
def _assert_safe_casting(cls, data, subarr):
223-
"""
224-
Ensure incoming data can be represented with matching signed-ness.
225-
"""
226-
if data.dtype.kind != cls._default_dtype.kind:
227-
if not np.array_equal(data, subarr):
228-
raise TypeError("Unsafe NumPy casting, you must explicitly cast")
229-
230-
def __contains__(self, key) -> bool:
231-
"""
232-
Check if key is a float and has a decimal. If it has, return False.
233-
"""
234-
hash(key)
235-
try:
236-
if is_float(key) and int(key) != key:
237-
# otherwise the `key in self._engine` check casts e.g. 1.1 -> 1
238-
return False
239-
return key in self._engine
240-
except (OverflowError, TypeError, ValueError):
241-
return False
242-
243-
@property
244-
def inferred_type(self) -> str:
245-
"""
246-
Always 'integer' for ``Int64Index`` and ``UInt64Index``
247-
"""
248-
return "integer"
249-
250-
@property
251-
def asi8(self) -> np.ndarray:
252-
# do not cache or you'll create a memory leak
253-
warnings.warn(
254-
"Index.asi8 is deprecated and will be removed in a future version",
255-
FutureWarning,
256-
stacklevel=2,
257-
)
258-
return self._values.view(self._default_dtype)
259-
260-
261-
class Int64Index(IntegerIndex):
262-
__doc__ = _num_index_shared_docs["class_descr"] % _int64_descr_args
263-
264-
_typ = "int64index"
265-
_engine_type = libindex.Int64Engine
266-
_default_dtype = np.dtype(np.int64)
267-
_dtype_validation_metadata = (is_signed_integer_dtype, "signed integer")
268-
269-
270-
_uint64_descr_args = {
271-
"klass": "UInt64Index",
272-
"ltype": "unsigned integer",
273-
"dtype": "uint64",
274-
"extra": "",
275-
}
276-
277-
278-
class UInt64Index(IntegerIndex):
279-
__doc__ = _num_index_shared_docs["class_descr"] % _uint64_descr_args
280-
281-
_typ = "uint64index"
282-
_engine_type = libindex.UInt64Engine
283-
_default_dtype = np.dtype(np.uint64)
284-
_dtype_validation_metadata = (is_unsigned_integer_dtype, "unsigned integer")
285-
286-
# ----------------------------------------------------------------
287-
# Indexing Methods
288-
289-
@doc(Index._convert_arr_indexer)
290-
def _convert_arr_indexer(self, keyarr):
291-
# Cast the indexer to uint64 if possible so that the values returned
292-
# from indexing are also uint64.
293-
dtype = None
294-
if is_integer_dtype(keyarr) or (
295-
lib.infer_dtype(keyarr, skipna=False) == "integer"
296-
):
297-
dtype = np.dtype(np.uint64)
298-
299-
return com.asarray_tuplesafe(keyarr, dtype=dtype)
300-
301-
302-
_float64_descr_args = {
303-
"klass": "Float64Index",
304-
"dtype": "float64",
305-
"ltype": "float",
306-
"extra": "",
307-
}
308-
309-
310-
class Float64Index(NumericIndex):
311-
__doc__ = _num_index_shared_docs["class_descr"] % _float64_descr_args
312-
313-
_typ = "float64index"
314-
_engine_type = libindex.Float64Engine
315-
_default_dtype = np.dtype(np.float64)
316-
_dtype_validation_metadata = (is_float_dtype, "float")
317-
318-
@property
319-
def inferred_type(self) -> str:
320-
"""
321-
Always 'floating' for ``Float64Index``
322-
"""
323-
return "floating"
324-
325-
@doc(Index.astype)
326-
def astype(self, dtype, copy=True):
327-
dtype = pandas_dtype(dtype)
328-
if needs_i8_conversion(dtype):
329-
raise TypeError(
330-
f"Cannot convert Float64Index to dtype {dtype}; integer "
331-
"values are required for conversion"
332-
)
333-
elif is_integer_dtype(dtype) and not is_extension_array_dtype(dtype):
334-
# TODO(jreback); this can change once we have an EA Index type
335-
# GH 13149
336-
arr = astype_nansafe(self._values, dtype=dtype)
337-
return Int64Index(arr, name=self.name)
338-
return super().astype(dtype, copy=copy)
339-
340-
# ----------------------------------------------------------------
341-
# Indexing Methods
342-
343-
@doc(Index._should_fallback_to_positional)
344-
def _should_fallback_to_positional(self) -> bool:
345-
return False
346-
347-
@doc(Index._convert_slice_indexer)
348-
def _convert_slice_indexer(self, key: slice, kind: str):
349-
assert kind in ["loc", "getitem"]
350-
351-
# We always treat __getitem__ slicing as label-based
352-
# translate to locations
353-
return self.slice_indexer(key.start, key.stop, key.step, kind=kind)
354-
355-
# ----------------------------------------------------------------
356-
357-
def _format_native_types(
358-
self, na_rep="", float_format=None, decimal=".", quoting=None, **kwargs
359-
):
360-
from pandas.io.formats.format import FloatArrayFormatter
361-
362-
formatter = FloatArrayFormatter(
363-
self._values,
364-
na_rep=na_rep,
365-
float_format=float_format,
366-
decimal=decimal,
367-
quoting=quoting,
368-
fixed_width=False,
369-
)
370-
return formatter.get_result_as_array()
371-
372208

373209
_numindex_descr_args = {
374210
"klass": "NumIndex",
@@ -382,7 +218,6 @@ class NumIndex(NumericIndex):
382218
__doc__ = _num_index_shared_docs["class_descr"] % _numindex_descr_args
383219

384220
_typ = "numindex"
385-
_default_dtype: np.dtype
386221
_dtype_validation_metadata = (is_numeric_dtype, "numeric type")
387222

388223
@cache_readonly
@@ -416,21 +251,13 @@ def inferred_type(self) -> str:
416251
}[self.dtype.kind]
417252

418253
@classmethod
419-
def _ensure_array(cls, data, dtype, copy: bool) -> np.ndarray:
420-
"""
421-
Ensure we have a valid array to pass to _simple_new.
422-
"""
423-
arr = np.array(data, dtype=dtype, copy=copy)
424-
cls._validate_dtype(arr.dtype)
425-
return arr
426-
427-
@classmethod
428-
def _assert_safe_casting(cls, data, subarr):
254+
def _assert_safe_casting(cls, data, subarr) -> None:
429255
"""
430256
Ensure incoming data can be represented with matching signed-ness.
431257
"""
432-
if data.dtype.kind not in {"i", "u"} and not np.array_equal(data, subarr):
433-
raise TypeError("Unsafe NumPy casting, you must explicitly cast")
258+
if is_integer_dtype(subarr.dtype):
259+
if not np.array_equal(data, subarr):
260+
raise TypeError("Unsafe NumPy casting, you must explicitly cast")
434261

435262
def __contains__(self, key) -> bool:
436263
"""
@@ -450,11 +277,7 @@ def __contains__(self, key) -> bool:
450277

451278
@doc(Index.astype)
452279
def astype(self, dtype, copy=True):
453-
if is_categorical_dtype(dtype):
454-
from pandas import CategoricalIndex
455-
456-
return CategoricalIndex(self, name=self.name, dtype=dtype, copy=copy)
457-
elif is_float_dtype(dtype):
280+
if is_float_dtype(self.dtype):
458281
dtype = pandas_dtype(dtype)
459282
if needs_i8_conversion(dtype):
460283
raise TypeError(
@@ -465,17 +288,52 @@ def astype(self, dtype, copy=True):
465288
# TODO(jreback); this can change once we have an EA Index type
466289
# GH 13149
467290
arr = astype_nansafe(self._values, dtype=dtype)
468-
return type(self)(arr, name=self.name, dtype=dtype)
291+
if isinstance(self, Float64Index):
292+
return Int64Index(arr, name=self.name)
293+
else:
294+
return NumIndex(arr, name=self.name, dtype=dtype)
295+
elif is_categorical_dtype(dtype):
296+
from pandas import CategoricalIndex
297+
298+
return CategoricalIndex(self, name=self.name, dtype=dtype, copy=copy)
469299

470300
return super().astype(dtype, copy=copy)
471301

302+
# ----------------------------------------------------------------
303+
# Indexing Methods
304+
305+
@doc(Index._should_fallback_to_positional)
306+
def _should_fallback_to_positional(self) -> bool:
307+
if self.inferred_type == "floating":
308+
return False
309+
310+
return super()._should_fallback_to_positional()
311+
472312
@doc(Index._convert_slice_indexer)
473313
def _convert_slice_indexer(self, key: slice, kind: str):
474-
assert kind in ["loc", "getitem"]
314+
if is_float_dtype(self.dtype):
315+
assert kind in ["loc", "getitem"]
316+
317+
# We always treat __getitem__ slicing as label-based
318+
# translate to locations
319+
return self.slice_indexer(key.start, key.stop, key.step, kind=kind)
320+
321+
return super()._convert_slice_indexer(key, kind=kind)
322+
323+
@doc(Index._convert_arr_indexer)
324+
def _convert_arr_indexer(self, keyarr) -> np.ndarray:
325+
if is_unsigned_integer_dtype(self.dtype):
326+
# Cast the indexer to uint64 if possible so that the values returned
327+
# from indexing are also uint64.
328+
dtype = None
329+
if is_integer_dtype(keyarr) or (
330+
lib.infer_dtype(keyarr, skipna=False) == "integer"
331+
):
332+
dtype = np.dtype(np.uint64)
475333

476-
# We always treat __getitem__ slicing as label-based
477-
# translate to locations
478-
return self.slice_indexer(key.start, key.stop, key.step, kind=kind)
334+
return com.asarray_tuplesafe(keyarr, dtype=dtype)
335+
336+
return super()._convert_arr_indexer(keyarr)
479337

480338
# ----------------------------------------------------------------
481339

@@ -502,3 +360,67 @@ def _format_native_types(
502360
fixed_width=False,
503361
)
504362
return formatter.get_result_as_array()
363+
364+
365+
_int64_descr_args = {
366+
"klass": "Int64Index",
367+
"ltype": "integer",
368+
"dtype": "int64",
369+
"extra": "",
370+
}
371+
372+
373+
class Int64Index(NumIndex):
374+
__doc__ = _num_index_shared_docs["class_descr"] % _int64_descr_args
375+
376+
_typ = "int64index"
377+
_engine_type = libindex.Int64Engine
378+
_default_dtype = np.dtype(np.int64)
379+
_dtype_validation_metadata = (is_signed_integer_dtype, "signed integer")
380+
381+
@property
382+
def asi8(self) -> np.ndarray:
383+
# do not cache or you'll create a memory leak
384+
warnings.warn(
385+
"Index.asi8 is deprecated and will be removed in a future version",
386+
FutureWarning,
387+
stacklevel=2,
388+
)
389+
return self._values.view(self._default_dtype)
390+
391+
392+
_uint64_descr_args = {
393+
"klass": "UInt64Index",
394+
"ltype": "unsigned integer",
395+
"dtype": "uint64",
396+
"extra": "",
397+
}
398+
399+
400+
class UInt64Index(NumIndex):
401+
__doc__ = _num_index_shared_docs["class_descr"] % _uint64_descr_args
402+
403+
_typ = "uint64index"
404+
_engine_type = libindex.UInt64Engine
405+
_default_dtype = np.dtype(np.uint64)
406+
_dtype_validation_metadata = (is_unsigned_integer_dtype, "unsigned integer")
407+
408+
# ----------------------------------------------------------------
409+
# Indexing Methods
410+
411+
412+
_float64_descr_args = {
413+
"klass": "Float64Index",
414+
"dtype": "float64",
415+
"ltype": "float",
416+
"extra": "",
417+
}
418+
419+
420+
class Float64Index(NumIndex):
421+
__doc__ = _num_index_shared_docs["class_descr"] % _float64_descr_args
422+
423+
_typ = "float64index"
424+
_engine_type = libindex.Float64Engine
425+
_default_dtype = np.dtype(np.float64)
426+
_dtype_validation_metadata = (is_float_dtype, "float")

0 commit comments

Comments
 (0)