diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 346258fe5162b..21811aa503612 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3384,7 +3384,7 @@ def _get_fill_indexer( else: indexer = self._get_fill_indexer_searchsorted(target, method, limit) if tolerance is not None and len(self): - indexer = self._filter_indexer_tolerance(target._values, indexer, tolerance) + indexer = self._filter_indexer_tolerance(target_values, indexer, tolerance) return indexer @final @@ -3434,15 +3434,10 @@ def _get_nearest_indexer(self, target: Index, limit, tolerance) -> np.ndarray: left_indexer = self.get_indexer(target, "pad", limit=limit) right_indexer = self.get_indexer(target, "backfill", limit=limit) - target_values = target._values - # error: Unsupported left operand type for - ("ExtensionArray") - left_distances = np.abs( - self._values[left_indexer] - target_values # type: ignore[operator] - ) - # error: Unsupported left operand type for - ("ExtensionArray") - right_distances = np.abs( - self._values[right_indexer] - target_values # type: ignore[operator] - ) + target_values = target._get_engine_target() + own_values = self._get_engine_target() + left_distances = np.abs(own_values[left_indexer] - target_values) + right_distances = np.abs(own_values[right_indexer] - target_values) op = operator.lt if self.is_monotonic_increasing else operator.le indexer = np.where( @@ -3461,8 +3456,8 @@ def _filter_indexer_tolerance( indexer: np.ndarray, tolerance, ) -> np.ndarray: - # error: Unsupported left operand type for - ("ExtensionArray") - distance = abs(self._values[indexer] - target) # type: ignore[operator] + own_values = self._get_engine_target() + distance = abs(own_values[indexer] - target) return np.where(distance <= tolerance, indexer, -1) # -------------------------------------------------------------------- diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 55317938d528d..82295bd8fbc4d 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -231,10 +231,7 @@ def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs): def _convert_tolerance(self, tolerance, target): tolerance = np.asarray(to_timedelta(tolerance).to_numpy()) - - if target.size != tolerance.size and tolerance.size > 1: - raise ValueError("list-like tolerance size must match target index size") - return tolerance + return super()._convert_tolerance(tolerance, target) def tolist(self) -> List: """ diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index d6427aed6edf3..13ef2eba2abaa 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -119,9 +119,8 @@ def _shallow_copy(self, values=None, name: Hashable = lib.no_default): return super()._shallow_copy(values=values, name=name) def _convert_tolerance(self, tolerance, target): - tolerance = np.asarray(tolerance) - if target.size != tolerance.size and tolerance.size > 1: - raise ValueError("list-like tolerance size must match target index size") + tolerance = super()._convert_tolerance(tolerance, target) + if not np.issubdtype(tolerance.dtype, np.number): if tolerance.ndim > 0: raise ValueError( diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 879f2ddbcfcd1..a9561cc477d4a 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -11,7 +11,7 @@ from pandas._libs.tslibs.parsing import DateParseError, parse_time_string from pandas._typing import Dtype, DtypeObj from pandas.errors import InvalidIndexError -from pandas.util._decorators import cache_readonly, doc +from pandas.util._decorators import doc from pandas.core.dtypes.common import ( is_bool_dtype, @@ -324,10 +324,6 @@ def __contains__(self, key: Any) -> bool: except KeyError: return False - @cache_readonly - def _int64index(self) -> Int64Index: - return Int64Index._simple_new(self.asi8, name=self.name) - # ------------------------------------------------------------------------ # Index Methods @@ -424,24 +420,18 @@ def inferred_type(self) -> str: # ------------------------------------------------------------------------ # Indexing Methods - def _get_indexer(self, target: Index, method=None, limit=None, tolerance=None): - - if not self._should_compare(target): - return self._get_indexer_non_comparable(target, method, unique=True) - - if isinstance(target, PeriodIndex): - target = target._int64index # i.e. target.asi8 - self_index = self._int64index - else: - self_index = self + def _convert_tolerance(self, tolerance, target): + # Returned tolerance must be in dtype/units so that + # `|self._get_engine_target() - target._engine_target()| <= tolerance` + # is meaningful. Since PeriodIndex returns int64 for engine_target, + # we may need to convert timedelta64 tolerance to int64. + tolerance = super()._convert_tolerance(tolerance, target) - if tolerance is not None: - tolerance = self._convert_tolerance(tolerance, target) - if self_index is not self: - # convert tolerance to i8 - tolerance = self._maybe_convert_timedelta(tolerance) + if self.dtype == target.dtype: + # convert tolerance to i8 + tolerance = self._maybe_convert_timedelta(tolerance) - return Index._get_indexer(self_index, target, method, limit, tolerance) + return tolerance def get_loc(self, key, method=None, tolerance=None): """ @@ -579,14 +569,6 @@ def _get_string_slice(self, key: str): except KeyError as err: raise KeyError(key) from err - # ------------------------------------------------------------------------ - - def memory_usage(self, deep: bool = False) -> int: - result = super().memory_usage(deep=deep) - if hasattr(self, "_cache") and "_int64index" in self._cache: - result += self._int64index.memory_usage(deep=deep) - return result - def period_range( start=None, end=None, periods: Optional[int] = None, freq=None, name=None diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 0f73b62faf6e7..360f342bc6832 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -3,7 +3,7 @@ from datetime import timedelta import operator from sys import getsizeof -from typing import Any, Hashable, List, Optional, Tuple +from typing import TYPE_CHECKING, Any, Hashable, List, Optional, Tuple import warnings import numpy as np @@ -20,7 +20,6 @@ ensure_python_int, is_float, is_integer, - is_list_like, is_scalar, is_signed_integer_dtype, is_timedelta64_dtype, @@ -35,6 +34,9 @@ from pandas.core.indexes.numeric import Float64Index, Int64Index from pandas.core.ops.common import unpack_zerodim_and_defer +if TYPE_CHECKING: + from pandas import Index + _empty_range = range(0) @@ -368,8 +370,8 @@ def get_loc(self, key, method=None, tolerance=None): raise KeyError(key) return super().get_loc(key, method=method, tolerance=tolerance) - def _get_indexer(self, target, method=None, limit=None, tolerance=None): - if com.any_not_none(method, tolerance, limit) or not is_list_like(target): + def _get_indexer(self, target: Index, method=None, limit=None, tolerance=None): + if com.any_not_none(method, tolerance, limit): return super()._get_indexer( target, method=method, tolerance=tolerance, limit=limit ) @@ -381,11 +383,11 @@ def _get_indexer(self, target, method=None, limit=None, tolerance=None): reverse = self._range[::-1] start, stop, step = reverse.start, reverse.stop, reverse.step - target_array = np.asarray(target) - if not (is_signed_integer_dtype(target_array) and target_array.ndim == 1): + if not is_signed_integer_dtype(target): # checks/conversions/roundings are delegated to general method return super()._get_indexer(target, method=method, tolerance=tolerance) + target_array = np.asarray(target) locs = target_array - start valid = (locs % step == 0) & (locs >= 0) & (target_array < stop) locs[~valid] = -1 diff --git a/pandas/tests/indexes/period/test_constructors.py b/pandas/tests/indexes/period/test_constructors.py index 75c8c766b0e67..c5018bd0e66d2 100644 --- a/pandas/tests/indexes/period/test_constructors.py +++ b/pandas/tests/indexes/period/test_constructors.py @@ -329,7 +329,7 @@ def test_constructor_simple_new(self): msg = "Should be numpy array of type i8" with pytest.raises(AssertionError, match=msg): # Need ndarray, not Int64Index - type(idx._data)._simple_new(idx._int64index, freq=idx.freq) + type(idx._data)._simple_new(Index(idx.asi8), freq=idx.freq) arr = type(idx._data)._simple_new(idx.asi8, freq=idx.freq) result = idx._simple_new(arr, name="p")