diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index a07226ef0f50a..f73fe77defc26 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6865,12 +6865,15 @@ def get_slice_bound(self, label, side: Literal["left", "right"]) -> int: # we need to look up the label try: slc = self.get_loc(label) - except KeyError as err: + except KeyError: try: return self._searchsorted_monotonic(label, side) except ValueError: - # raise the original KeyError - raise err from None + raise KeyError( + f"Cannot get {side} slice bound for non-monotonic index " + f"with a missing label {original_label!r}. " + "Either sort the index or specify an existing label." + ) from None if isinstance(slc, np.ndarray): # get_loc may return a boolean array, which diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index d11c2ef8a3a3c..2293ca375be70 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -564,9 +564,10 @@ def test_getitem_setitem_integer_slice_keyerrors(self): # non-monotonic, raise KeyError df2 = df.iloc[list(range(5)) + list(range(5, 10))[::-1]] - with pytest.raises(KeyError, match=r"^3$"): + msg = "non-monotonic index with a missing label 3" + with pytest.raises(KeyError, match=msg): df2.loc[3:11] - with pytest.raises(KeyError, match=r"^3$"): + with pytest.raises(KeyError, match=msg): df2.loc[3:11] = 0 def test_fancy_getitem_slice_mixed(self, float_frame, float_string_frame): diff --git a/pandas/tests/indexes/numeric/test_indexing.py b/pandas/tests/indexes/numeric/test_indexing.py index 2f37b15ca74f5..0a88f55b06af8 100644 --- a/pandas/tests/indexes/numeric/test_indexing.py +++ b/pandas/tests/indexes/numeric/test_indexing.py @@ -600,10 +600,11 @@ def test_slice_locs_na(self): def test_slice_locs_na_raises(self): index = Index([np.nan, 1, 2]) - with pytest.raises(KeyError, match="1.5"): + msg = "non-monotonic index with a missing label 1.5" + with pytest.raises(KeyError, match=msg): index.slice_locs(start=1.5) - with pytest.raises(KeyError, match="1.5"): + with pytest.raises(KeyError, match=msg): index.slice_locs(end=1.5) diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 91111d0b1b0ea..372120362b5d6 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -3162,9 +3162,10 @@ def test_loc_getitem_setitem_integer_slice_keyerrors(self): # non-monotonic, raise KeyError s2 = ser.iloc[list(range(5)) + list(range(9, 4, -1))] - with pytest.raises(KeyError, match=r"^3$"): + msg = "non-monotonic index with a missing label 3" + with pytest.raises(KeyError, match=msg): s2.loc[3:11] - with pytest.raises(KeyError, match=r"^3$"): + with pytest.raises(KeyError, match=msg): s2.loc[3:11] = 0 def test_loc_getitem_iterator(self, string_series): diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index 32ca3f1ba11c3..bffdae5977c23 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -404,6 +404,13 @@ def test_setitem_empty_indexer(indexer, val): tm.assert_frame_equal(df, expected) +def test_loc_non_monotonic_index_with_a_missing_label(): + msg = "Cannot get left slice bound for non-monotonic index with a missing label 4" + ser = Series([3, 6, 7, 6], index=[3, 8, 7, 6]) + with pytest.raises(KeyError, match=msg): + ser.loc[4:7] + + class TestDeprecatedIndexers: @pytest.mark.parametrize("key", [{1}, {1: 1}]) def test_getitem_dict_and_set_deprecated(self, key):