Skip to content

Commit 109ae10

Browse files
authored
BUG: RangeIndex.difference with mismatched step (#38028)
* BUG: return RangeIndex from RangeIndex.difference * test coverage * BUG: RangeIndex.difference with mismatched step * whatsnew
1 parent fe87624 commit 109ae10

File tree

3 files changed

+28
-6
lines changed

3 files changed

+28
-6
lines changed

doc/source/whatsnew/v1.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,7 @@ Other
772772
- Bug in :meth:`Index.union` behaving differently depending on whether operand is an :class:`Index` or other list-like (:issue:`36384`)
773773
- Passing an array with 2 or more dimensions to the :class:`Series` constructor now raises the more specific ``ValueError`` rather than a bare ``Exception`` (:issue:`35744`)
774774
- Bug in ``dir`` where ``dir(obj)`` wouldn't show attributes defined on the instance for pandas objects (:issue:`37173`)
775+
- Bug in :meth:`RangeIndex.difference` returning :class:`Int64Index` in some cases where it should return :class:`RangeIndex` (:issue:`38028`)
775776

776777
.. ---------------------------------------------------------------------------
777778

pandas/core/indexes/range.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -670,13 +670,17 @@ def difference(self, other, sort=None):
670670
if not isinstance(overlap, RangeIndex):
671671
# We wont end up with RangeIndex, so fall back
672672
return super().difference(other, sort=sort)
673+
if overlap.step != first.step:
674+
# In some cases we might be able to get a RangeIndex back,
675+
# but not worth the effort.
676+
return super().difference(other, sort=sort)
673677

674678
if overlap[0] == first.start:
675679
# The difference is everything after the intersection
676680
new_rng = range(overlap[-1] + first.step, first.stop, first.step)
677-
elif overlap[-1] == first.stop:
681+
elif overlap[-1] == first[-1]:
678682
# The difference is everything before the intersection
679-
new_rng = range(first.start, overlap[0] - first.step, first.step)
683+
new_rng = range(first.start, overlap[0], first.step)
680684
else:
681685
# The difference is not range-like
682686
return super().difference(other, sort=sort)

pandas/tests/indexes/ranges/test_setops.py

+21-4
Original file line numberDiff line numberDiff line change
@@ -247,21 +247,38 @@ def test_difference(self):
247247

248248
result = obj.difference(obj)
249249
expected = RangeIndex.from_range(range(0), name="foo")
250-
tm.assert_index_equal(result, expected)
250+
tm.assert_index_equal(result, expected, exact=True)
251251

252252
result = obj.difference(expected.rename("bar"))
253-
tm.assert_index_equal(result, obj.rename(None))
253+
tm.assert_index_equal(result, obj.rename(None), exact=True)
254254

255255
result = obj.difference(obj[:3])
256-
tm.assert_index_equal(result, obj[3:])
256+
tm.assert_index_equal(result, obj[3:], exact=True)
257257

258258
result = obj.difference(obj[-3:])
259-
tm.assert_index_equal(result, obj[:-3])
259+
tm.assert_index_equal(result, obj[:-3], exact=True)
260+
261+
result = obj[::-1].difference(obj[-3:])
262+
tm.assert_index_equal(result, obj[:-3][::-1], exact=True)
263+
264+
result = obj[::-1].difference(obj[-3:][::-1])
265+
tm.assert_index_equal(result, obj[:-3][::-1], exact=True)
260266

261267
result = obj.difference(obj[2:6])
262268
expected = Int64Index([1, 2, 7, 8, 9], name="foo")
263269
tm.assert_index_equal(result, expected)
264270

271+
def test_difference_mismatched_step(self):
272+
obj = RangeIndex.from_range(range(1, 10), name="foo")
273+
274+
result = obj.difference(obj[::2])
275+
expected = obj[1::2]._int64index
276+
tm.assert_index_equal(result, expected, exact=True)
277+
278+
result = obj.difference(obj[1::2])
279+
expected = obj[::2]._int64index
280+
tm.assert_index_equal(result, expected, exact=True)
281+
265282
def test_symmetric_difference(self):
266283
# GH#12034 Cases where we operate against another RangeIndex and may
267284
# get back another RangeIndex

0 commit comments

Comments
 (0)