diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 64ba18271100e..f23366193958f 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3410,7 +3410,7 @@ def get_indexer( if len(target) == 0: return np.array([], dtype=np.intp) - if not self._should_compare(target) and not is_interval_dtype(self.dtype): + if not self._should_compare(target) and not self._should_partial_index(target): # IntervalIndex get special treatment bc numeric scalars can be # matched to Interval scalars return self._get_indexer_non_comparable(target, method=method, unique=True) @@ -3478,6 +3478,16 @@ def _get_indexer( return ensure_platform_int(indexer) + @final + def _should_partial_index(self, target: Index) -> bool: + """ + Should we attempt partial-matching indexing? + """ + if is_interval_dtype(self.dtype): + # "Index" has no attribute "left" + return self.left._should_compare(target) # type: ignore[attr-defined] + return False + @final def _check_indexing_method( self, diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index e6764dd01e6d3..9225f128d1f26 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -646,9 +646,6 @@ def _get_indexer( # returned ndarray is np.intp if isinstance(target, IntervalIndex): - if not self._should_compare(target): - return self._get_indexer_non_comparable(target, method, unique=True) - # non-overlapping -> at most one match per interval in target # want exact matches -> need both left/right to match, so defer to # left/right get_indexer, compare elementwise, equality -> match @@ -656,12 +653,14 @@ def _get_indexer( right_indexer = self.right.get_indexer(target.right) indexer = np.where(left_indexer == right_indexer, left_indexer, -1) - elif not is_object_dtype(target): + elif not is_object_dtype(target.dtype): # homogeneous scalar index: use IntervalTree + # we should always have self._should_partial_index(target) here target = self._maybe_convert_i8(target) indexer = self._engine.get_indexer(target.values) else: # heterogeneous scalar index: defer elementwise to get_loc + # we should always have self._should_partial_index(target) here return self._get_indexer_pointwise(target)[0] return ensure_platform_int(indexer) @@ -671,11 +670,12 @@ def get_indexer_non_unique(self, target: Index) -> tuple[np.ndarray, np.ndarray] # both returned ndarrays are np.intp target = ensure_index(target) - if isinstance(target, IntervalIndex) and not self._should_compare(target): - # different closed or incompatible subtype -> no matches + if not self._should_compare(target) and not self._should_partial_index(target): + # e.g. IntervalIndex with different closed or incompatible subtype + # -> no matches return self._get_indexer_non_comparable(target, None, unique=False) - elif is_object_dtype(target.dtype) or isinstance(target, IntervalIndex): + elif is_object_dtype(target.dtype) or not self._should_partial_index(target): # target might contain intervals: defer elementwise to get_loc return self._get_indexer_pointwise(target)