Skip to content

Commit d4bd9f0

Browse files
committed
FIX: Index._searchsorted_monotonic for decreasing indexes
Fixed an issue where Index._searchsorted_monotonic(..., side='right') returns the left side position for monotonic decreasing indexes. Issue had a downstream effect on scalar lookups in IntervalIndex as well.
1 parent 96f92eb commit d4bd9f0

File tree

4 files changed

+32
-1
lines changed

4 files changed

+32
-1
lines changed

doc/source/whatsnew/v0.21.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ Indexing
358358
- Allow unicode empty strings as placeholders in multilevel columns in Python 2 (:issue:`17099`)
359359
- Bug in ``.iloc`` when used with inplace addition or assignment and an int indexer on a ``MultiIndex`` causing the wrong indexes to be read from and written to (:issue:`17148`)
360360
- Bug in ``.isin()`` in which checking membership in empty ``Series`` objects raised an error (:issue:`16991`)
361+
- Bug in ``IntervalIndex`` where performing a scalar lookup fails for included right endpoints of non-overlapping monotonic decreasing indexes (:issue:`16417`, :issue:`17271`)
361362

362363
I/O
363364
^^^

pandas/core/indexes/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3452,7 +3452,7 @@ def _searchsorted_monotonic(self, label, side='left'):
34523452
# everything for it to work (element ordering, search side and
34533453
# resulting value).
34543454
pos = self[::-1].searchsorted(label, side='right' if side == 'left'
3455-
else 'right')
3455+
else 'left')
34563456
return len(self) - pos
34573457

34583458
raise ValueError('index must be monotonic increasing or decreasing')

pandas/tests/indexes/test_base.py

+21
Original file line numberDiff line numberDiff line change
@@ -2103,3 +2103,24 @@ def test_intersect_str_dates(self):
21032103
res = i2.intersection(i1)
21042104

21052105
assert len(res) == 0
2106+
2107+
def test_searchsorted_monotonic(self):
2108+
# GH17271
2109+
idx_inc = Index([0, 2, 4])
2110+
idx_dec = Index([4, 2, 0])
2111+
2112+
# test included value.
2113+
assert idx_inc._searchsorted_monotonic(0, side='left') == 0
2114+
assert idx_inc._searchsorted_monotonic(0, side='right') == 1
2115+
assert idx_dec._searchsorted_monotonic(0, side='left') == 2
2116+
assert idx_dec._searchsorted_monotonic(0, side='right') == 3
2117+
2118+
# test non-included value.
2119+
for side in ('left', 'right'):
2120+
assert idx_inc._searchsorted_monotonic(1, side=side) == 1
2121+
assert idx_dec._searchsorted_monotonic(1, side=side) == 2
2122+
2123+
# non-monotonic should raise.
2124+
idx_bad = Index([0, 4, 2])
2125+
with pytest.raises(ValueError):
2126+
idx_bad._searchsorted_monotonic(3)

pandas/tests/indexing/test_interval.py

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ class TestIntervalIndex(object):
1111
def setup_method(self, method):
1212
self.s = Series(np.arange(5), IntervalIndex.from_breaks(np.arange(6)))
1313

14+
idx_dec = IntervalIndex.from_tuples([(2, 3), (1, 2), (0, 1)])
15+
self.s_dec = Series(list('abc'), idx_dec)
16+
1417
def test_loc_with_scalar(self):
1518

1619
s = self.s
@@ -39,6 +42,9 @@ def test_loc_with_scalar(self):
3942
expected = s.iloc[2:5]
4043
tm.assert_series_equal(expected, s.loc[s >= 2])
4144

45+
# test endpoint of non-overlapping monotonic decreasing (GH16417)
46+
assert self.s_dec.loc[3] == 'a'
47+
4248
def test_getitem_with_scalar(self):
4349

4450
s = self.s
@@ -67,6 +73,9 @@ def test_getitem_with_scalar(self):
6773
expected = s.iloc[2:5]
6874
tm.assert_series_equal(expected, s[s >= 2])
6975

76+
# test endpoint of non-overlapping monotonic decreasing (GH16417)
77+
assert self.s_dec[3] == 'a'
78+
7079
def test_with_interval(self):
7180

7281
s = self.s

0 commit comments

Comments
 (0)