From 8650d75f378bb28ae22bf8db0fddf796a6d07673 Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 1 Feb 2022 13:31:18 -0800 Subject: [PATCH] CLN: Index methods incorrectly assuming object dtype --- pandas/core/indexes/base.py | 30 ++++++++++++++++++++++++++++- pandas/core/indexes/datetimelike.py | 6 ------ pandas/core/indexes/interval.py | 8 -------- pandas/core/indexes/multi.py | 4 ---- pandas/core/indexes/numeric.py | 20 ------------------- 5 files changed, 29 insertions(+), 39 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 8bbaa7cddba62..f6feec5fd97a6 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -391,7 +391,6 @@ def _outer_indexer( _comparables: list[str] = ["name"] _attributes: list[str] = ["name"] _is_numeric_dtype: bool = False - _can_hold_na: bool = True _can_hold_strings: bool = True # Whether this index is a NumericIndex, but not a Int64Index, Float64Index, @@ -2206,6 +2205,20 @@ def _get_grouper_for_level(self, mapper, *, level=None): # -------------------------------------------------------------------- # Introspection Methods + @cache_readonly + @final + def _can_hold_na(self) -> bool: + if isinstance(self.dtype, ExtensionDtype): + if isinstance(self.dtype, IntervalDtype): + # FIXME(GH#45720): this is inaccurate for integer-backed + # IntervalArray, but without it other.categories.take raises + # in IntervalArray._cmp_method + return True + return self.dtype._can_hold_na + if self.dtype.kind in ["i", "u", "b"]: + return False + return True + @final @property def is_monotonic(self) -> bool: @@ -2662,10 +2675,21 @@ def inferred_type(self) -> str_t: return lib.infer_dtype(self._values, skipna=False) @cache_readonly + @final def _is_all_dates(self) -> bool: """ Whether or not the index values only consist of dates. """ + + if needs_i8_conversion(self.dtype): + return True + elif self.dtype != _dtype_obj: + # TODO(ExtensionIndex): 3rd party EA might override? + # Note: this includes IntervalIndex, even when the left/right + # contain datetime-like objects. + return False + elif self._is_multi: + return False return is_datetime_array(ensure_object(self._values)) @cache_readonly @@ -6159,6 +6183,10 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: """ Can we compare values of the given dtype to our own? """ + if self.dtype.kind == "b": + return dtype.kind == "b" + elif is_numeric_dtype(self.dtype): + return is_numeric_dtype(dtype) return True @final diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 8e94d72752b33..2731c9ab447b7 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -100,10 +100,6 @@ class DatetimeIndexOpsMixin(NDArrayBackedExtensionIndex): ), ) - @property - def _is_all_dates(self) -> bool: - return True - # ------------------------------------------------------------------------ def equals(self, other: Any) -> bool: @@ -151,8 +147,6 @@ def __contains__(self, key: Any) -> bool: return False return True - _can_hold_na = True - def _convert_tolerance(self, tolerance, target): tolerance = np.asarray(to_timedelta(tolerance).to_numpy()) return super()._convert_tolerance(tolerance, target) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 5250f19c839bf..1e39c1db1a73b 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -897,14 +897,6 @@ def _intersection_non_unique(self, other: IntervalIndex) -> IntervalIndex: # -------------------------------------------------------------------- - @property - def _is_all_dates(self) -> bool: - """ - This is False even when left/right contain datetime-like objects, - as the check is done on the Interval itself - """ - return False - def _get_engine_target(self) -> np.ndarray: # Note: we _could_ use libjoin functions by either casting to object # dtype or constructing tuples (faster than constructing Intervals) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index d65062419477c..3e2dc12fef24f 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1841,10 +1841,6 @@ def to_flat_index(self) -> Index: """ return Index(self._values, tupleize_cols=False) - @property - def _is_all_dates(self) -> bool: - return False - def is_lexsorted(self) -> bool: warnings.warn( "MultiIndex.is_lexsorted is deprecated as a public function, " diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index bc6c21ac52bc8..5922a39395ce9 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -14,7 +14,6 @@ ) from pandas._typing import ( Dtype, - DtypeObj, npt, ) from pandas.util._decorators import ( @@ -91,14 +90,6 @@ class NumericIndex(Index): _can_hold_strings = False _is_backward_compat_public_numeric_index: bool = True - # error: Signature of "_can_hold_na" incompatible with supertype "Index" - @cache_readonly - def _can_hold_na(self) -> bool: # type: ignore[override] - if is_float_dtype(self.dtype): - return True - else: - return False - _engine_types: dict[np.dtype, type[libindex.IndexEngine]] = { np.dtype(np.int8): libindex.Int8Engine, np.dtype(np.int16): libindex.Int16Engine, @@ -268,10 +259,6 @@ def _convert_tolerance(self, tolerance, target): ) return tolerance - def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: - # If we ever have BoolIndex or ComplexIndex, this may need to be tightened - return is_numeric_dtype(dtype) - @classmethod def _assert_safe_casting(cls, data: np.ndarray, subarr: np.ndarray) -> None: """ @@ -284,13 +271,6 @@ def _assert_safe_casting(cls, data: np.ndarray, subarr: np.ndarray) -> None: if not np.array_equal(data, subarr): raise TypeError("Unsafe NumPy casting, you must explicitly cast") - @property - def _is_all_dates(self) -> bool: - """ - Checks that all the labels are datetime objects. - """ - return False - def _format_native_types( self, *, na_rep="", float_format=None, decimal=".", quoting=None, **kwargs ):