Skip to content

Commit a30e7c0

Browse files
author
tp
committed
IntervalIndex.get_loc can now look for exact matches only
1 parent 6ba65ca commit a30e7c0

File tree

4 files changed

+29
-11
lines changed

4 files changed

+29
-11
lines changed

doc/source/whatsnew/v0.23.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ Other API Changes
313313
- :func:`DatetimeIndex.shift` and :func:`TimedeltaIndex.shift` will now raise ``NullFrequencyError`` (which subclasses ``ValueError``, which was raised in older versions) when the index object frequency is ``None`` (:issue:`19147`)
314314
- Addition and subtraction of ``NaN`` from a :class:`Series` with ``dtype='timedelta64[ns]'`` will raise a ``TypeError` instead of treating the ``NaN`` as ``NaT`` (:issue:`19274`)
315315
- Set operations (union, difference...) on :class:`IntervalIndex` with incompatible index types will now raise a ``TypeError`` rather than a ``ValueError`` (:issue:`19329`)
316+
- :meth:`IntervalIndex.get_loc` can now find exact matches only by setting ``method='exact'`` (:issue:`19349`)
316317

317318
.. _whatsnew_0230.deprecations:
318319

pandas/core/indexes/interval.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ def _maybe_cast_indexed(self, key):
837837
return key
838838

839839
def _check_method(self, method):
840-
if method is None:
840+
if method in {None, 'exact'}:
841841
return
842842

843843
if method in ['bfill', 'backfill', 'pad', 'ffill', 'nearest']:
@@ -867,17 +867,13 @@ def _searchsorted_monotonic(self, label, side, exclude_label=False):
867867

868868
return sub_idx._searchsorted_monotonic(label, side)
869869

870-
def _get_loc_only_exact_matches(self, key):
870+
def _get_loc_only_exact_matches(self, key, unique=True):
871871
if isinstance(key, Interval):
872-
873-
if not self.is_unique:
872+
if unique and not self.is_unique:
874873
raise ValueError("cannot index with a slice Interval"
875874
" and a non-unique index")
876-
877-
# TODO: this expands to a tuple index, see if we can
878-
# do better
879-
return Index(self._multiindex.values).get_loc(key)
880-
raise KeyError
875+
return super(IntervalIndex, self)._engine.get_loc(key)
876+
raise KeyError(key)
881877

882878
def _find_non_overlapping_monotonic_bounds(self, key):
883879
if isinstance(key, IntervalMixin):
@@ -911,8 +907,9 @@ def get_loc(self, key, method=None):
911907
Parameters
912908
----------
913909
key : label
914-
method : {None}, optional
910+
method : {None, 'exact'}, optional
915911
* default: matches where the label is within an interval only.
912+
* exact: exact matches only.
916913
917914
Returns
918915
-------
@@ -940,12 +937,22 @@ def get_loc(self, key, method=None):
940937
>>> overlapping_index = pd.IntervalIndex.from_intervals([i2, i3])
941938
>>> overlapping_index.get_loc(1.5)
942939
array([0, 1], dtype=int64)
940+
941+
If you want to find exact matches only, use ``method='exact'``:
942+
943+
>>> index.get_loc(i1, method='exact')
944+
0
945+
>>> index.get_loc(pd.Interval(0,2), method='exact')
946+
KeyError: Interval(0,2)
943947
"""
944948
self._check_method(method)
945949

946950
original_key = key
947951
key = self._maybe_cast_indexed(key)
948952

953+
if method == 'exact':
954+
return self._get_loc_only_exact_matches(key, unique=False)
955+
949956
if self.is_non_overlapping_monotonic:
950957
if isinstance(key, Interval):
951958
left = self._maybe_cast_slice_bound(key.left, 'left', None)

pandas/tests/indexes/interval/test_interval.py

+10
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,10 @@ def test_get_loc_value(self):
666666
idx = IntervalIndex.from_arrays([0, 2], [1, 3])
667667
pytest.raises(KeyError, idx.get_loc, 1.5)
668668

669+
# GH19349
670+
with pytest.raises(KeyError):
671+
self.index.get_loc(0.5, method='exact')
672+
669673
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
670674
def slice_locs_cases(self, breaks):
671675
# TODO: same tests for more index types
@@ -739,6 +743,12 @@ def test_get_loc_interval(self):
739743
pytest.raises(KeyError, self.index.get_loc, Interval(2, 3))
740744
pytest.raises(KeyError, self.index.get_loc,
741745
Interval(-1, 0, 'left'))
746+
# GH19349
747+
assert self.index.get_loc(Interval(0, 1), method='exact') == 0
748+
with pytest.raises(KeyError):
749+
self.index.get_loc(Interval(0, 0.5), method='exact')
750+
with pytest.raises(KeyError):
751+
self.index.get_loc(Interval(0, 1, closed='neither'), method='exact')
742752

743753
# To be removed, replaced by test_interval_new.py (see #16316, #16386)
744754
def test_get_indexer(self):

pandas/tests/indexing/interval/test_interval.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def test_with_slices(self):
136136
s[Interval(3, 6):]
137137

138138
expected = s.iloc[3:5]
139-
result = s[[Interval(3, 6)]]
139+
result = s[Interval(3, 6)]
140140
tm.assert_series_equal(expected, result)
141141

142142
# slice of scalar with step != 1

0 commit comments

Comments
 (0)