Skip to content

Commit 0a294f8

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 e8a1765 commit 0a294f8

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
@@ -359,6 +359,7 @@ Indexing
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`)
361361
- Bug in ``CategoricalIndex`` reindexing in which specified indices containing duplicates were not being respected (:issue:`17323`)
362+
- Bug in ``IntervalIndex`` where performing a scalar lookup fails for included right endpoints of non-overlapping monotonic decreasing indexes (:issue:`16417`, :issue:`17271`)
362363

363364
I/O
364365
^^^

pandas/core/indexes/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3457,7 +3457,7 @@ def _searchsorted_monotonic(self, label, side='left'):
34573457
# everything for it to work (element ordering, search side and
34583458
# resulting value).
34593459
pos = self[::-1].searchsorted(label, side='right' if side == 'left'
3460-
else 'right')
3460+
else 'left')
34613461
return len(self) - pos
34623462

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

pandas/tests/indexes/test_base.py

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

21142114
assert len(res) == 0
2115+
2116+
def test_searchsorted_monotonic(self):
2117+
# GH17271
2118+
idx_inc = Index([0, 2, 4])
2119+
idx_dec = Index([4, 2, 0])
2120+
2121+
# test included value.
2122+
assert idx_inc._searchsorted_monotonic(0, side='left') == 0
2123+
assert idx_inc._searchsorted_monotonic(0, side='right') == 1
2124+
assert idx_dec._searchsorted_monotonic(0, side='left') == 2
2125+
assert idx_dec._searchsorted_monotonic(0, side='right') == 3
2126+
2127+
# test non-included value.
2128+
for side in ('left', 'right'):
2129+
assert idx_inc._searchsorted_monotonic(1, side=side) == 1
2130+
assert idx_dec._searchsorted_monotonic(1, side=side) == 2
2131+
2132+
# non-monotonic should raise.
2133+
idx_bad = Index([0, 4, 2])
2134+
with pytest.raises(ValueError):
2135+
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)