From 4dc6785217009eca432ddfc8e5ee2824507434f7 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 1 Jul 2021 16:15:30 -0700 Subject: [PATCH] DEPR: Index.get_loc with method (#42269) --- doc/source/whatsnew/v1.4.0.rst | 2 +- pandas/core/indexes/base.py | 27 ++++++++++++++++--- .../tests/indexes/datetimes/test_indexing.py | 10 +++++-- pandas/tests/indexes/numeric/test_indexing.py | 23 +++++++++++----- pandas/tests/indexes/object/test_indexing.py | 6 +++-- pandas/tests/indexes/period/test_indexing.py | 2 ++ .../tests/indexes/timedeltas/test_indexing.py | 1 + pandas/tests/test_downstream.py | 6 ++++- 8 files changed, 61 insertions(+), 16 deletions(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 31e9a3d9873b2..abb6d0630ff9d 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -97,7 +97,7 @@ Other API changes Deprecations ~~~~~~~~~~~~ - Deprecated :meth:`Index.is_type_compatible` (:issue:`42113`) -- +- Deprecated ``method`` argument in :meth:`Index.get_loc`, use ``index.get_indexer([label], method=...)`` instead (:issue:`42269`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index bfccd475090b6..6070d6863039e 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3358,6 +3358,15 @@ def get_loc(self, key, method=None, tolerance=None): except KeyError as err: raise KeyError(key) from err + # GH#42269 + warnings.warn( + f"Passing method to {type(self).__name__}.get_loc is deprecated " + "and will raise in a future version. Use " + "index.get_indexer([item], method=...) instead", + FutureWarning, + stacklevel=2, + ) + if is_scalar(key) and isna(key) and not self.hasnans: raise KeyError(key) @@ -4948,14 +4957,24 @@ def asof(self, label): Traceback (most recent call last): ValueError: index must be monotonic increasing or decreasing """ + self._searchsorted_monotonic(label) # validate sortedness try: - loc = self.get_loc(label, method="pad") - except KeyError: - return self._na_value + loc = self.get_loc(label) + except (KeyError, TypeError): + # KeyError -> No exact match, try for padded + # TypeError -> passed e.g. non-hashable, fall through to get + # the tested exception message + indexer = self.get_indexer([label], method="pad") + if indexer.ndim > 1 or indexer.size > 1: + raise TypeError("asof requires scalar valued input") + loc = indexer.item() + if loc == -1: + return self._na_value else: if isinstance(loc, slice): loc = loc.indices(len(self))[-1] - return self[loc] + + return self[loc] def asof_locs(self, where: Index, mask: np.ndarray) -> np.ndarray: """ diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index de6fa4e8f4238..d705fa7f0ed2c 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -422,6 +422,7 @@ def test_take_fill_value_with_timezone(self): class TestGetLoc: @pytest.mark.parametrize("method", [None, "pad", "backfill", "nearest"]) + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc_method_exact_match(self, method): idx = date_range("2000-01-01", periods=3) assert idx.get_loc(idx[1], method) == 1 @@ -431,6 +432,7 @@ def test_get_loc_method_exact_match(self, method): if method is not None: assert idx.get_loc(idx[1], method, tolerance=pd.Timedelta("0 days")) == 1 + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc(self): idx = date_range("2000-01-01", periods=3) @@ -498,7 +500,8 @@ def test_get_loc(self): ) msg = "cannot yet lookup inexact labels when key is a time object" with pytest.raises(NotImplementedError, match=msg): - idx.get_loc(time(12, 30), method="pad") + with tm.assert_produces_warning(FutureWarning, match="deprecated"): + idx.get_loc(time(12, 30), method="pad") def test_get_loc_time_nat(self): # GH#35114 @@ -518,7 +521,10 @@ def test_get_loc_tz_aware(self): freq="5s", ) key = Timestamp("2019-12-12 10:19:25", tz="US/Eastern") - result = dti.get_loc(key, method="nearest") + with tm.assert_produces_warning( + FutureWarning, match="deprecated", check_stacklevel=False + ): + result = dti.get_loc(key, method="nearest") assert result == 7433 def test_get_loc_nat(self): diff --git a/pandas/tests/indexes/numeric/test_indexing.py b/pandas/tests/indexes/numeric/test_indexing.py index 540dbde609470..e6b418868dbeb 100644 --- a/pandas/tests/indexes/numeric/test_indexing.py +++ b/pandas/tests/indexes/numeric/test_indexing.py @@ -24,12 +24,17 @@ class TestGetLoc: @pytest.mark.parametrize("method", [None, "pad", "backfill", "nearest"]) def test_get_loc(self, method): index = Index([0, 1, 2]) - assert index.get_loc(1, method=method) == 1 + warn = None if method is None else FutureWarning + + with tm.assert_produces_warning(warn, match="deprecated"): + assert index.get_loc(1, method=method) == 1 if method: - assert index.get_loc(1, method=method, tolerance=0) == 1 + with tm.assert_produces_warning(warn, match="deprecated"): + assert index.get_loc(1, method=method, tolerance=0) == 1 @pytest.mark.parametrize("method", [None, "pad", "backfill", "nearest"]) + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc_raises_bad_label(self, method): index = Index([0, 1, 2]) if method: @@ -43,6 +48,7 @@ def test_get_loc_raises_bad_label(self, method): @pytest.mark.parametrize( "method,loc", [("pad", 1), ("backfill", 2), ("nearest", 1)] ) + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc_tolerance(self, method, loc): index = Index([0, 1, 2]) assert index.get_loc(1.1, method) == loc @@ -52,12 +58,14 @@ def test_get_loc_tolerance(self, method, loc): def test_get_loc_outside_tolerance_raises(self, method): index = Index([0, 1, 2]) with pytest.raises(KeyError, match="1.1"): - index.get_loc(1.1, method, tolerance=0.05) + with tm.assert_produces_warning(FutureWarning, match="deprecated"): + index.get_loc(1.1, method, tolerance=0.05) def test_get_loc_bad_tolerance_raises(self): index = Index([0, 1, 2]) with pytest.raises(ValueError, match="must be numeric"): - index.get_loc(1.1, "nearest", tolerance="invalid") + with tm.assert_produces_warning(FutureWarning, match="deprecated"): + index.get_loc(1.1, "nearest", tolerance="invalid") def test_get_loc_tolerance_no_method_raises(self): index = Index([0, 1, 2]) @@ -67,8 +75,10 @@ def test_get_loc_tolerance_no_method_raises(self): def test_get_loc_raises_missized_tolerance(self): index = Index([0, 1, 2]) with pytest.raises(ValueError, match="tolerance size must match"): - index.get_loc(1.1, "nearest", tolerance=[1, 1]) + with tm.assert_produces_warning(FutureWarning, match="deprecated"): + index.get_loc(1.1, "nearest", tolerance=[1, 1]) + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc_float64(self): idx = Float64Index([0.0, 1.0, 2.0]) for method in [None, "pad", "backfill", "nearest"]: @@ -139,7 +149,8 @@ def test_get_loc_float_index_nan_with_method(self, vals, method): # GH#39382 idx = Index(vals) with pytest.raises(KeyError, match="nan"): - idx.get_loc(np.nan, method=method) + with tm.assert_produces_warning(FutureWarning, match="deprecated"): + idx.get_loc(np.nan, method=method) class TestGetIndexer: diff --git a/pandas/tests/indexes/object/test_indexing.py b/pandas/tests/indexes/object/test_indexing.py index a683e9faed1f2..b26676a0d83cf 100644 --- a/pandas/tests/indexes/object/test_indexing.py +++ b/pandas/tests/indexes/object/test_indexing.py @@ -10,12 +10,14 @@ class TestGetLoc: def test_get_loc_raises_object_nearest(self): index = Index(["a", "c"]) with pytest.raises(TypeError, match="unsupported operand type"): - index.get_loc("a", method="nearest") + with tm.assert_produces_warning(FutureWarning, match="deprecated"): + index.get_loc("a", method="nearest") def test_get_loc_raises_object_tolerance(self): index = Index(["a", "c"]) with pytest.raises(TypeError, match="unsupported operand type"): - index.get_loc("a", method="pad", tolerance="invalid") + with tm.assert_produces_warning(FutureWarning, match="deprecated"): + index.get_loc("a", method="pad", tolerance="invalid") class TestGetIndexer: diff --git a/pandas/tests/indexes/period/test_indexing.py b/pandas/tests/indexes/period/test_indexing.py index a41d02cfbd394..526ef3808a51c 100644 --- a/pandas/tests/indexes/period/test_indexing.py +++ b/pandas/tests/indexes/period/test_indexing.py @@ -339,6 +339,7 @@ def test_get_loc_integer(self): # TODO: This method came from test_period; de-dup with version above @pytest.mark.parametrize("method", [None, "pad", "backfill", "nearest"]) + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc_method(self, method): idx = period_range("2000-01-01", periods=3) @@ -352,6 +353,7 @@ def test_get_loc_method(self, method): idx.get_loc(key, method=method) # TODO: This method came from test_period; de-dup with version above + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc3(self): idx = period_range("2000-01-01", periods=5)[::2] diff --git a/pandas/tests/indexes/timedeltas/test_indexing.py b/pandas/tests/indexes/timedeltas/test_indexing.py index ec41956371164..669bbe23af559 100644 --- a/pandas/tests/indexes/timedeltas/test_indexing.py +++ b/pandas/tests/indexes/timedeltas/test_indexing.py @@ -82,6 +82,7 @@ def test_timestamp_invalid_key(self, key): class TestGetLoc: + @pytest.mark.filterwarnings("ignore:Passing method:FutureWarning") def test_get_loc(self): idx = to_timedelta(["0 days", "1 days", "2 days"]) diff --git a/pandas/tests/test_downstream.py b/pandas/tests/test_downstream.py index ea95f90d3a2cb..6a7d64235f11c 100644 --- a/pandas/tests/test_downstream.py +++ b/pandas/tests/test_downstream.py @@ -59,7 +59,11 @@ def test_xarray_cftimeindex_nearest(): import xarray times = xarray.cftime_range("0001", periods=2) - result = times.get_loc(cftime.DatetimeGregorian(2000, 1, 1), method="nearest") + key = cftime.DatetimeGregorian(2000, 1, 1) + with tm.assert_produces_warning( + FutureWarning, match="deprecated", check_stacklevel=False + ): + result = times.get_loc(key, method="nearest") expected = 1 assert result == expected