Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: intersection of decreasing RangeIndexes #17374

Merged
merged 1 commit into from
Sep 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ Indexing
- 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`)
- Bug in ``.isin()`` in which checking membership in empty ``Series`` objects raised an error (:issue:`16991`)
- Bug in ``CategoricalIndex`` reindexing in which specified indices containing duplicates were not being respected (:issue:`17323`)
- Bug in intersection of ``RangeIndex`` with negative step (:issue:`17296`)

I/O
^^^
Expand Down
22 changes: 13 additions & 9 deletions pandas/core/indexes/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,34 +324,38 @@ def intersection(self, other):
if not len(self) or not len(other):
return RangeIndex._simple_new(None)

first = self[::-1] if self._step < 0 else self
second = other[::-1] if other._step < 0 else other

# check whether intervals intersect
# deals with in- and decreasing ranges
int_low = max(min(self._start, self._stop + 1),
min(other._start, other._stop + 1))
int_high = min(max(self._stop, self._start + 1),
max(other._stop, other._start + 1))
int_low = max(first._start, second._start)
int_high = min(first._stop, second._stop)
if int_high <= int_low:
return RangeIndex._simple_new(None)

# Method hint: linear Diophantine equation
# solve intersection problem
# performance hint: for identical step sizes, could use
# cheaper alternative
gcd, s, t = self._extended_gcd(self._step, other._step)
gcd, s, t = first._extended_gcd(first._step, second._step)

# check whether element sets intersect
if (self._start - other._start) % gcd:
if (first._start - second._start) % gcd:
return RangeIndex._simple_new(None)

# calculate parameters for the RangeIndex describing the
# intersection disregarding the lower bounds
tmp_start = self._start + (other._start - self._start) * \
self._step // gcd * s
new_step = self._step * other._step // gcd
tmp_start = first._start + (second._start - first._start) * \
first._step // gcd * s
new_step = first._step * second._step // gcd
new_index = RangeIndex(tmp_start, int_high, new_step, fastpath=True)

# adjust index to limiting interval
new_index._start = new_index._min_fitting_element(int_low)

if (self._step < 0 and other._step < 0) is not (new_index._step < 0):
new_index = new_index[::-1]
return new_index

def _min_fitting_element(self, lower_limit):
Expand Down
15 changes: 15 additions & 0 deletions pandas/tests/indexes/test_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,21 @@ def test_intersection(self):
other.values)))
tm.assert_index_equal(result, expected)

# reversed (GH 17296)
result = other.intersection(self.index)
tm.assert_index_equal(result, expected)

# GH 17296: intersect two decreasing RangeIndexes
first = RangeIndex(10, -2, -2)
other = RangeIndex(5, -4, -1)
expected = first.astype(int).intersection(other.astype(int))
result = first.intersection(other).astype(int)
tm.assert_index_equal(result, expected)

# reversed
result = other.intersection(first).astype(int)
tm.assert_index_equal(result, expected)

index = RangeIndex(5)

# intersect of non-overlapping indices
Expand Down