diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index b5900ead246f3..7827cd9bfe332 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -5523,6 +5523,17 @@ def _cmp_method(self, other, op): """ Wrapper used to dispatch comparison operations. """ + if self.is_(other): + # fastpath + if op in {operator.eq, operator.le, operator.ge}: + arr = np.ones(len(self), dtype=bool) + if self._can_hold_na and not isinstance(self, ABCMultiIndex): + # TODO: should set MultiIndex._can_hold_na = False? + arr[self.isna()] = False + return arr + elif op in {operator.ne, operator.lt, operator.gt}: + return np.zeros(len(self), dtype=bool) + if isinstance(other, (np.ndarray, Index, ABCSeries, ExtensionArray)): if len(self) != len(other): raise ValueError("Lengths must match to compare") diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index 7778b1e264cd8..8a1bcc7146616 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -1,4 +1,3 @@ -import operator from typing import Any import warnings @@ -6,7 +5,7 @@ from pandas._libs import index as libindex, lib from pandas._typing import Dtype, Label -from pandas.util._decorators import cache_readonly, doc +from pandas.util._decorators import doc from pandas.core.dtypes.cast import astype_nansafe from pandas.core.dtypes.common import ( @@ -188,18 +187,6 @@ def _union(self, other, sort): else: return super()._union(other, sort) - def _cmp_method(self, other, op): - if self.is_(other): # fastpath - if op in {operator.eq, operator.le, operator.ge}: - arr = np.ones(len(self), dtype=bool) - if self._can_hold_na: - arr[self.isna()] = False - return arr - elif op in {operator.ne, operator.lt, operator.gt}: - return np.zeros(len(self), dtype=bool) - - return super()._cmp_method(other, op) - _num_index_shared_docs[ "class_descr" @@ -243,6 +230,20 @@ class IntegerIndex(NumericIndex): """ _default_dtype: np.dtype + _can_hold_na = False + + @classmethod + def _assert_safe_casting(cls, data, subarr): + """ + Ensure incoming data can be represented with matching signed-ness. + """ + if data.dtype.kind != cls._default_dtype.kind: + if not np.array_equal(data, subarr): + raise TypeError("Unsafe NumPy casting, you must explicitly cast") + + def _can_union_without_object_cast(self, other) -> bool: + # See GH#26778, further casting may occur in NumericIndex._union + return other.dtype == "f8" or other.dtype == self.dtype def __contains__(self, key) -> bool: """ @@ -278,23 +279,9 @@ class Int64Index(IntegerIndex): __doc__ = _num_index_shared_docs["class_descr"] % _int64_descr_args _typ = "int64index" - _can_hold_na = False _engine_type = libindex.Int64Engine _default_dtype = np.dtype(np.int64) - @classmethod - def _assert_safe_casting(cls, data, subarr): - """ - Ensure incoming data can be represented as ints. - """ - if not issubclass(data.dtype.type, np.signedinteger): - if not np.array_equal(data, subarr): - raise TypeError("Unsafe NumPy casting, you must explicitly cast") - - def _can_union_without_object_cast(self, other) -> bool: - # See GH#26778, further casting may occur in NumericIndex._union - return other.dtype == "f8" or other.dtype == self.dtype - _uint64_descr_args = dict( klass="UInt64Index", ltype="unsigned integer", dtype="uint64", extra="" @@ -305,7 +292,6 @@ class UInt64Index(IntegerIndex): __doc__ = _num_index_shared_docs["class_descr"] % _uint64_descr_args _typ = "uint64index" - _can_hold_na = False _engine_type = libindex.UInt64Engine _default_dtype = np.dtype(np.uint64) @@ -324,21 +310,6 @@ def _convert_arr_indexer(self, keyarr): return com.asarray_tuplesafe(keyarr, dtype=dtype) - # ---------------------------------------------------------------- - - @classmethod - def _assert_safe_casting(cls, data, subarr): - """ - Ensure incoming data can be represented as uints. - """ - if not issubclass(data.dtype.type, np.unsignedinteger): - if not np.array_equal(data, subarr): - raise TypeError("Unsafe NumPy casting, you must explicitly cast") - - def _can_union_without_object_cast(self, other) -> bool: - # See GH#26778, further casting may occur in NumericIndex._union - return other.dtype == "f8" or other.dtype == self.dtype - _float64_descr_args = dict( klass="Float64Index", dtype="float64", ltype="float", extra="" @@ -350,7 +321,7 @@ class Float64Index(NumericIndex): _typ = "float64index" _engine_type = libindex.Float64Engine - _default_dtype = np.float64 + _default_dtype = np.dtype(np.float64) @property def inferred_type(self) -> str: @@ -429,10 +400,6 @@ def __contains__(self, other: Any) -> bool: return is_float(other) and np.isnan(other) and self.hasnans - @cache_readonly - def is_unique(self) -> bool: - return super().is_unique and self._nan_idxs.size < 2 - def _can_union_without_object_cast(self, other) -> bool: # See GH#26778, further casting may occur in NumericIndex._union return is_numeric_dtype(other.dtype)