@@ -466,6 +466,9 @@ def equals(self, other: object) -> bool:
466
466
return self ._range == other ._range
467
467
return super ().equals (other )
468
468
469
+ # --------------------------------------------------------------------
470
+ # Set Operations
471
+
469
472
def intersection (self , other , sort = False ):
470
473
"""
471
474
Form the intersection of two Index objects.
@@ -632,6 +635,57 @@ def _union(self, other, sort):
632
635
return type (self )(start_r , end_r + step_o , step_o )
633
636
return self ._int64index ._union (other , sort = sort )
634
637
638
+ def difference (self , other , sort = None ):
639
+ # optimized set operation if we have another RangeIndex
640
+ self ._validate_sort_keyword (sort )
641
+
642
+ if not isinstance (other , RangeIndex ):
643
+ return super ().difference (other , sort = sort )
644
+
645
+ res_name = ops .get_op_result_name (self , other )
646
+
647
+ first = self ._range [::- 1 ] if self .step < 0 else self ._range
648
+ overlap = self .intersection (other )
649
+ if overlap .step < 0 :
650
+ overlap = overlap [::- 1 ]
651
+
652
+ if len (overlap ) == 0 :
653
+ return self ._shallow_copy (name = res_name )
654
+ if len (overlap ) == len (self ):
655
+ return self [:0 ].rename (res_name )
656
+ if not isinstance (overlap , RangeIndex ):
657
+ # We wont end up with RangeIndex, so fall back
658
+ return super ().difference (other , sort = sort )
659
+
660
+ if overlap [0 ] == first .start :
661
+ # The difference is everything after the intersection
662
+ new_rng = range (overlap [- 1 ] + first .step , first .stop , first .step )
663
+ elif overlap [- 1 ] == first .stop :
664
+ # The difference is everything before the intersection
665
+ new_rng = range (first .start , overlap [0 ] - first .step , first .step )
666
+ else :
667
+ # The difference is not range-like
668
+ return super ().difference (other , sort = sort )
669
+
670
+ new_index = type (self )._simple_new (new_rng , name = res_name )
671
+ if first is not self ._range :
672
+ new_index = new_index [::- 1 ]
673
+ return new_index
674
+
675
+ def symmetric_difference (self , other , result_name = None , sort = None ):
676
+ if not isinstance (other , RangeIndex ) or sort is not None :
677
+ return super ().symmetric_difference (other , result_name , sort )
678
+
679
+ left = self .difference (other )
680
+ right = other .difference (self )
681
+ result = left .union (right )
682
+
683
+ if result_name is not None :
684
+ result = result .rename (result_name )
685
+ return result
686
+
687
+ # --------------------------------------------------------------------
688
+
635
689
@doc (Int64Index .join )
636
690
def join (self , other , how = "left" , level = None , return_indexers = False , sort = False ):
637
691
if how == "outer" and self is not other :
@@ -744,12 +798,17 @@ def __floordiv__(self, other):
744
798
return self ._simple_new (new_range , name = self .name )
745
799
return self ._int64index // other
746
800
801
+ # --------------------------------------------------------------------
802
+ # Reductions
803
+
747
804
def all (self ) -> bool :
748
805
return 0 not in self ._range
749
806
750
807
def any (self ) -> bool :
751
808
return any (self ._range )
752
809
810
+ # --------------------------------------------------------------------
811
+
753
812
@classmethod
754
813
def _add_numeric_methods_binary (cls ):
755
814
""" add in numeric methods, specialized to RangeIndex """
0 commit comments