From 2e92bc94cfe09e9048ab9186e7c5852377854053 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 15 Nov 2020 11:09:42 -0800 Subject: [PATCH 1/3] REF: copy IntervalIndex._check_method for CategoricalIndex --- pandas/core/indexes/category.py | 24 +++++++++++-------- pandas/core/indexes/interval.py | 2 +- .../indexes/categorical/test_indexing.py | 8 +++---- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 06df8f85cded7..4791056a4e597 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -524,24 +524,28 @@ def _reindex_non_unique(self, target): def _maybe_cast_indexer(self, key) -> int: return self._data._unbox_scalar(key) + def _check_method(self, method): + # TODO: share with IntervalIndex? + if method is None: + return + + if method in ["bfill", "backfill", "pad", "ffill", "nearest"]: + raise NotImplementedError( + f"method {method} not yet implemented for {type(self).__name__}" + ) + + raise ValueError("Invalid fill method") + @Appender(_index_shared_docs["get_indexer"] % _index_doc_kwargs) def get_indexer(self, target, method=None, limit=None, tolerance=None): method = missing.clean_reindex_fill_method(method) target = ibase.ensure_index(target) + self._check_method(method) + if self.is_unique and self.equals(target): return np.arange(len(self), dtype="intp") - if method == "pad" or method == "backfill": - raise NotImplementedError( - "method='pad' and method='backfill' not " - "implemented yet for CategoricalIndex" - ) - elif method == "nearest": - raise NotImplementedError( - "method='nearest' not implemented yet for CategoricalIndex" - ) - # Note: we use engine.get_indexer_non_unique below because, even if # `target` is unique, any non-category entries in it will be encoded # as -1 by _get_codes_for_get_indexer, so `codes` may not be unique. diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 2aec86c9cdfae..5b8a5fa8372ef 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -588,7 +588,7 @@ def _check_method(self, method): if method in ["bfill", "backfill", "pad", "ffill", "nearest"]: raise NotImplementedError( - f"method {method} not yet implemented for IntervalIndex" + f"method {method} not yet implemented for {type(self).__name__}" ) raise ValueError("Invalid fill method") diff --git a/pandas/tests/indexes/categorical/test_indexing.py b/pandas/tests/indexes/categorical/test_indexing.py index 9f4b7f7bad10f..cf9360821d37f 100644 --- a/pandas/tests/indexes/categorical/test_indexing.py +++ b/pandas/tests/indexes/categorical/test_indexing.py @@ -238,16 +238,14 @@ def test_get_indexer(self): r1 = idx1.get_indexer(idx2) tm.assert_almost_equal(r1, np.array([0, 1, 2, -1], dtype=np.intp)) - msg = ( - "method='pad' and method='backfill' not implemented yet for " - "CategoricalIndex" - ) + msg = "method pad not yet implemented for CategoricalIndex" with pytest.raises(NotImplementedError, match=msg): idx2.get_indexer(idx1, method="pad") + msg = "method backfill not yet implemented for CategoricalIndex" with pytest.raises(NotImplementedError, match=msg): idx2.get_indexer(idx1, method="backfill") - msg = "method='nearest' not implemented yet for CategoricalIndex" + msg = "method nearest not yet implemented for CategoricalIndex" with pytest.raises(NotImplementedError, match=msg): idx2.get_indexer(idx1, method="nearest") From aa7513225c93d3059de16e1cc727cc231ebc92d9 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 15 Nov 2020 11:58:47 -0800 Subject: [PATCH 2/3] Share check_indexing_method --- pandas/core/indexes/category.py | 29 ++++++++++++++++------------- pandas/core/indexes/interval.py | 16 +++------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 4791056a4e597..06069a05a552c 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -524,24 +524,12 @@ def _reindex_non_unique(self, target): def _maybe_cast_indexer(self, key) -> int: return self._data._unbox_scalar(key) - def _check_method(self, method): - # TODO: share with IntervalIndex? - if method is None: - return - - if method in ["bfill", "backfill", "pad", "ffill", "nearest"]: - raise NotImplementedError( - f"method {method} not yet implemented for {type(self).__name__}" - ) - - raise ValueError("Invalid fill method") - @Appender(_index_shared_docs["get_indexer"] % _index_doc_kwargs) def get_indexer(self, target, method=None, limit=None, tolerance=None): method = missing.clean_reindex_fill_method(method) target = ibase.ensure_index(target) - self._check_method(method) + check_indexing_method(self, method) if self.is_unique and self.equals(target): return np.arange(len(self), dtype="intp") @@ -708,3 +696,18 @@ def _delegate_method(self, name: str, *args, **kwargs): if is_scalar(res): return res return CategoricalIndex(res, name=self.name) + + +def check_indexing_method(self, method): + """ + Raise if we have a get_indexer `method` that is not supported or valid. + """ + if method is None: + return + + if method in ["bfill", "backfill", "pad", "ffill", "nearest"]: + raise NotImplementedError( + f"method {method} not yet implemented for {type(self).__name__}" + ) + + raise ValueError("Invalid fill method") diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 5b8a5fa8372ef..cf5d4a59a6fe4 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -51,6 +51,7 @@ ensure_index, maybe_extract_name, ) +from pandas.core.indexes.category import check_indexing_method from pandas.core.indexes.datetimes import DatetimeIndex, date_range from pandas.core.indexes.extension import ExtensionIndex, inherit_names from pandas.core.indexes.multi import MultiIndex @@ -582,17 +583,6 @@ def _maybe_convert_i8(self, key): return key_i8 - def _check_method(self, method): - if method is None: - return - - if method in ["bfill", "backfill", "pad", "ffill", "nearest"]: - raise NotImplementedError( - f"method {method} not yet implemented for {type(self).__name__}" - ) - - raise ValueError("Invalid fill method") - def _searchsorted_monotonic(self, label, side, exclude_label=False): if not self.is_non_overlapping_monotonic: raise KeyError( @@ -663,7 +653,7 @@ def get_loc( >>> index.get_loc(pd.Interval(0, 1)) 0 """ - self._check_method(method) + check_indexing_method(self, method) if not is_scalar(key): raise InvalidIndexError(key) @@ -714,7 +704,7 @@ def get_indexer( tolerance: Optional[Any] = None, ) -> np.ndarray: - self._check_method(method) + check_indexing_method(self, method) if self.is_overlapping: raise InvalidIndexError( From 453e79180c85518507ac8f64ca215c15ba97f8c1 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 15 Nov 2020 15:49:38 -0800 Subject: [PATCH 3/3] make method --- pandas/core/indexes/category.py | 17 +---------------- pandas/core/indexes/extension.py | 15 +++++++++++++++ pandas/core/indexes/interval.py | 5 ++--- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 06069a05a552c..d875cd682030b 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -529,7 +529,7 @@ def get_indexer(self, target, method=None, limit=None, tolerance=None): method = missing.clean_reindex_fill_method(method) target = ibase.ensure_index(target) - check_indexing_method(self, method) + self._check_indexing_method(method) if self.is_unique and self.equals(target): return np.arange(len(self), dtype="intp") @@ -696,18 +696,3 @@ def _delegate_method(self, name: str, *args, **kwargs): if is_scalar(res): return res return CategoricalIndex(res, name=self.name) - - -def check_indexing_method(self, method): - """ - Raise if we have a get_indexer `method` that is not supported or valid. - """ - if method is None: - return - - if method in ["bfill", "backfill", "pad", "ffill", "nearest"]: - raise NotImplementedError( - f"method {method} not yet implemented for {type(self).__name__}" - ) - - raise ValueError("Invalid fill method") diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index 4d09a97b18eed..c117c32f26d25 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -230,6 +230,21 @@ def __getitem__(self, key): # --------------------------------------------------------------------- + def _check_indexing_method(self, method): + """ + Raise if we have a get_indexer `method` that is not supported or valid. + """ + # GH#37871 for now this is only for IntervalIndex and CategoricalIndex + if method is None: + return + + if method in ["bfill", "backfill", "pad", "ffill", "nearest"]: + raise NotImplementedError( + f"method {method} not yet implemented for {type(self).__name__}" + ) + + raise ValueError("Invalid fill method") + def _get_engine_target(self) -> np.ndarray: return np.asarray(self._data) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index cf5d4a59a6fe4..e6174c63fbc15 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -51,7 +51,6 @@ ensure_index, maybe_extract_name, ) -from pandas.core.indexes.category import check_indexing_method from pandas.core.indexes.datetimes import DatetimeIndex, date_range from pandas.core.indexes.extension import ExtensionIndex, inherit_names from pandas.core.indexes.multi import MultiIndex @@ -653,7 +652,7 @@ def get_loc( >>> index.get_loc(pd.Interval(0, 1)) 0 """ - check_indexing_method(self, method) + self._check_indexing_method(method) if not is_scalar(key): raise InvalidIndexError(key) @@ -704,7 +703,7 @@ def get_indexer( tolerance: Optional[Any] = None, ) -> np.ndarray: - check_indexing_method(self, method) + self._check_indexing_method(method) if self.is_overlapping: raise InvalidIndexError(