diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 4def2e4b93553..b209a6556709b 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -705,6 +705,10 @@ def _difference(self, other, sort=None): else: return super()._difference(other, sort=sort) + elif len(overlap) == 2 and overlap[0] == first[0] and overlap[-1] == first[-1]: + # e.g. range(-8, 20, 7) and range(13, -9, -3) + return self[1:-1] + if overlap.step == first.step: if overlap[0] == first.start: # The difference is everything after the intersection @@ -712,6 +716,10 @@ def _difference(self, other, sort=None): elif overlap[-1] == first[-1]: # The difference is everything before the intersection new_rng = range(first.start, overlap[0], first.step) + elif overlap._range == first[1:-1]: + # e.g. range(4) and range(1, 3) + step = len(first) - 1 + new_rng = first[::step] else: # The difference is not range-like # e.g. range(1, 10, 1) and range(3, 7, 1) @@ -725,16 +733,19 @@ def _difference(self, other, sort=None): if overlap.step == first.step * 2: if overlap[0] == first[0] and overlap[-1] in (first[-1], first[-2]): # e.g. range(1, 10, 1) and range(1, 10, 2) - return self[1::2] + new_rng = first[1::2] elif overlap[0] == first[1] and overlap[-1] in (first[-1], first[-2]): # e.g. range(1, 10, 1) and range(2, 10, 2) - return self[::2] + new_rng = first[::2] - # We can get here with e.g. range(20) and range(0, 10, 2) + else: + # We can get here with e.g. range(20) and range(0, 10, 2) + return super()._difference(other, sort=sort) - # e.g. range(10) and range(0, 10, 3) - return super()._difference(other, sort=sort) + else: + # e.g. range(10) and range(0, 10, 3) + return super()._difference(other, sort=sort) new_index = type(self)._simple_new(new_rng, name=res_name) if first is not self._range: diff --git a/pandas/tests/indexes/ranges/test_setops.py b/pandas/tests/indexes/ranges/test_setops.py index 583391bd96a85..2942010af2720 100644 --- a/pandas/tests/indexes/ranges/test_setops.py +++ b/pandas/tests/indexes/ranges/test_setops.py @@ -377,6 +377,24 @@ def test_difference_mismatched_step(self): result = obj[::-1].difference(obj[1::2], sort=False) tm.assert_index_equal(result, expected[::-1], exact=True) + def test_difference_interior_overlap_endpoints_preserved(self): + left = RangeIndex(range(4)) + right = RangeIndex(range(1, 3)) + + result = left.difference(right) + expected = RangeIndex(0, 4, 3) + assert expected.tolist() == [0, 3] + tm.assert_index_equal(result, expected, exact=True) + + def test_difference_endpoints_overlap_interior_preserved(self): + left = RangeIndex(-8, 20, 7) + right = RangeIndex(13, -9, -3) + + result = left.difference(right) + expected = RangeIndex(-1, 13, 7) + assert expected.tolist() == [-1, 6] + tm.assert_index_equal(result, expected, exact=True) + def test_difference_interior_non_preserving(self): # case with intersection of length 1 but RangeIndex is not preserved idx = Index(range(10))