33
33
)
34
34
import pandas .core .indexes .base as ibase
35
35
from pandas .core .indexes .base import Index , _index_shared_docs
36
+ from pandas .core .indexes .numeric import Int64Index
36
37
from pandas .core .tools .timedeltas import to_timedelta
37
38
38
39
from pandas .tseries .frequencies import DateOffset , to_offset
@@ -71,36 +72,6 @@ def method(self, other):
71
72
return method
72
73
73
74
74
- class DatetimeTimedeltaMixin :
75
- """
76
- Mixin class for methods shared by DatetimeIndex and TimedeltaIndex,
77
- but not PeriodIndex
78
- """
79
-
80
- def _set_freq (self , freq ):
81
- """
82
- Set the _freq attribute on our underlying DatetimeArray.
83
-
84
- Parameters
85
- ----------
86
- freq : DateOffset, None, or "infer"
87
- """
88
- # GH#29843
89
- if freq is None :
90
- # Always valid
91
- pass
92
- elif len (self ) == 0 and isinstance (freq , DateOffset ):
93
- # Always valid. In the TimedeltaIndex case, we assume this
94
- # is a Tick offset.
95
- pass
96
- else :
97
- # As an internal method, we can ensure this assertion always holds
98
- assert freq == "infer"
99
- freq = to_offset (self .inferred_freq )
100
-
101
- self ._data ._freq = freq
102
-
103
-
104
75
class DatetimeIndexOpsMixin (ExtensionOpsMixin ):
105
76
"""
106
77
Common ops mixin to support a unified interface datetimelike Index.
@@ -125,6 +96,10 @@ class DatetimeIndexOpsMixin(ExtensionOpsMixin):
125
96
__iter__ = ea_passthrough (DatetimeLikeArrayMixin .__iter__ )
126
97
mean = ea_passthrough (DatetimeLikeArrayMixin .mean )
127
98
99
+ @property
100
+ def is_all_dates (self ) -> bool :
101
+ return True
102
+
128
103
@property
129
104
def freq (self ):
130
105
"""
@@ -605,66 +580,6 @@ def isin(self, values, level=None):
605
580
606
581
return algorithms .isin (self .asi8 , values .asi8 )
607
582
608
- def intersection (self , other , sort = False ):
609
- self ._validate_sort_keyword (sort )
610
- self ._assert_can_do_setop (other )
611
-
612
- if self .equals (other ):
613
- return self ._get_reconciled_name_object (other )
614
-
615
- if len (self ) == 0 :
616
- return self .copy ()
617
- if len (other ) == 0 :
618
- return other .copy ()
619
-
620
- if not isinstance (other , type (self )):
621
- result = Index .intersection (self , other , sort = sort )
622
- if isinstance (result , type (self )):
623
- if result .freq is None :
624
- result ._set_freq ("infer" )
625
- return result
626
-
627
- elif (
628
- other .freq is None
629
- or self .freq is None
630
- or other .freq != self .freq
631
- or not other .freq .is_anchored ()
632
- or (not self .is_monotonic or not other .is_monotonic )
633
- ):
634
- result = Index .intersection (self , other , sort = sort )
635
-
636
- # Invalidate the freq of `result`, which may not be correct at
637
- # this point, depending on the values.
638
-
639
- result ._set_freq (None )
640
- if hasattr (self , "tz" ):
641
- result = self ._shallow_copy (
642
- result ._values , name = result .name , tz = result .tz , freq = None
643
- )
644
- else :
645
- result = self ._shallow_copy (result ._values , name = result .name , freq = None )
646
- if result .freq is None :
647
- result ._set_freq ("infer" )
648
- return result
649
-
650
- # to make our life easier, "sort" the two ranges
651
- if self [0 ] <= other [0 ]:
652
- left , right = self , other
653
- else :
654
- left , right = other , self
655
-
656
- # after sorting, the intersection always starts with the right index
657
- # and ends with the index of which the last elements is smallest
658
- end = min (left [- 1 ], right [- 1 ])
659
- start = right [0 ]
660
-
661
- if end < start :
662
- return type (self )(data = [])
663
- else :
664
- lslice = slice (* left .slice_locs (start , end ))
665
- left_chunk = left .values [lslice ]
666
- return self ._shallow_copy (left_chunk )
667
-
668
583
@Appender (_index_shared_docs ["repeat" ] % _index_doc_kwargs )
669
584
def repeat (self , repeats , axis = None ):
670
585
nv .validate_repeat (tuple (), dict (axis = axis ))
@@ -777,6 +692,168 @@ def shift(self, periods=1, freq=None):
777
692
return type (self )(result , name = self .name )
778
693
779
694
695
+ class DatetimeTimedeltaMixin (DatetimeIndexOpsMixin , Int64Index ):
696
+ """
697
+ Mixin class for methods shared by DatetimeIndex and TimedeltaIndex,
698
+ but not PeriodIndex
699
+ """
700
+
701
+ # Compat for frequency inference, see GH#23789
702
+ _is_monotonic_increasing = Index .is_monotonic_increasing
703
+ _is_monotonic_decreasing = Index .is_monotonic_decreasing
704
+ _is_unique = Index .is_unique
705
+
706
+ def _set_freq (self , freq ):
707
+ """
708
+ Set the _freq attribute on our underlying DatetimeArray.
709
+
710
+ Parameters
711
+ ----------
712
+ freq : DateOffset, None, or "infer"
713
+ """
714
+ # GH#29843
715
+ if freq is None :
716
+ # Always valid
717
+ pass
718
+ elif len (self ) == 0 and isinstance (freq , DateOffset ):
719
+ # Always valid. In the TimedeltaIndex case, we assume this
720
+ # is a Tick offset.
721
+ pass
722
+ else :
723
+ # As an internal method, we can ensure this assertion always holds
724
+ assert freq == "infer"
725
+ freq = to_offset (self .inferred_freq )
726
+
727
+ self ._data ._freq = freq
728
+
729
+ # --------------------------------------------------------------------
730
+ # Set Operation Methods
731
+
732
+ @Appender (Index .difference .__doc__ )
733
+ def difference (self , other , sort = None ):
734
+ new_idx = super ().difference (other , sort = sort )
735
+ new_idx ._set_freq (None )
736
+ return new_idx
737
+
738
+ def intersection (self , other , sort = False ):
739
+ """
740
+ Specialized intersection for DatetimeIndex/TimedeltaIndex.
741
+
742
+ May be much faster than Index.intersection
743
+
744
+ Parameters
745
+ ----------
746
+ other : Same type as self or array-like
747
+ sort : False or None, default False
748
+ Sort the resulting index if possible.
749
+
750
+ .. versionadded:: 0.24.0
751
+
752
+ .. versionchanged:: 0.24.1
753
+
754
+ Changed the default to ``False`` to match the behaviour
755
+ from before 0.24.0.
756
+
757
+ .. versionchanged:: 0.25.0
758
+
759
+ The `sort` keyword is added
760
+
761
+ Returns
762
+ -------
763
+ y : Index or same type as self
764
+ """
765
+ self ._validate_sort_keyword (sort )
766
+ self ._assert_can_do_setop (other )
767
+
768
+ if self .equals (other ):
769
+ return self ._get_reconciled_name_object (other )
770
+
771
+ if len (self ) == 0 :
772
+ return self .copy ()
773
+ if len (other ) == 0 :
774
+ return other .copy ()
775
+
776
+ if not isinstance (other , type (self )):
777
+ result = Index .intersection (self , other , sort = sort )
778
+ if isinstance (result , type (self )):
779
+ if result .freq is None :
780
+ result ._set_freq ("infer" )
781
+ return result
782
+
783
+ elif (
784
+ other .freq is None
785
+ or self .freq is None
786
+ or other .freq != self .freq
787
+ or not other .freq .is_anchored ()
788
+ or (not self .is_monotonic or not other .is_monotonic )
789
+ ):
790
+ result = Index .intersection (self , other , sort = sort )
791
+
792
+ # Invalidate the freq of `result`, which may not be correct at
793
+ # this point, depending on the values.
794
+
795
+ result ._set_freq (None )
796
+ if hasattr (self , "tz" ):
797
+ result = self ._shallow_copy (
798
+ result ._values , name = result .name , tz = result .tz , freq = None
799
+ )
800
+ else :
801
+ result = self ._shallow_copy (result ._values , name = result .name , freq = None )
802
+ if result .freq is None :
803
+ result ._set_freq ("infer" )
804
+ return result
805
+
806
+ # to make our life easier, "sort" the two ranges
807
+ if self [0 ] <= other [0 ]:
808
+ left , right = self , other
809
+ else :
810
+ left , right = other , self
811
+
812
+ # after sorting, the intersection always starts with the right index
813
+ # and ends with the index of which the last elements is smallest
814
+ end = min (left [- 1 ], right [- 1 ])
815
+ start = right [0 ]
816
+
817
+ if end < start :
818
+ return type (self )(data = [])
819
+ else :
820
+ lslice = slice (* left .slice_locs (start , end ))
821
+ left_chunk = left .values [lslice ]
822
+ return self ._shallow_copy (left_chunk )
823
+
824
+ def _can_fast_union (self , other ) -> bool :
825
+ if not isinstance (other , type (self )):
826
+ return False
827
+
828
+ freq = self .freq
829
+
830
+ if freq is None or freq != other .freq :
831
+ return False
832
+
833
+ if not self .is_monotonic or not other .is_monotonic :
834
+ return False
835
+
836
+ if len (self ) == 0 or len (other ) == 0 :
837
+ return True
838
+
839
+ # to make our life easier, "sort" the two ranges
840
+ if self [0 ] <= other [0 ]:
841
+ left , right = self , other
842
+ else :
843
+ left , right = other , self
844
+
845
+ right_start = right [0 ]
846
+ left_end = left [- 1 ]
847
+
848
+ # Only need to "adjoin", not overlap
849
+ try :
850
+ return (right_start == left_end + freq ) or right_start in left
851
+ except ValueError :
852
+ # if we are comparing a freq that does not propagate timezones
853
+ # this will raise
854
+ return False
855
+
856
+
780
857
def wrap_arithmetic_op (self , other , result ):
781
858
if result is NotImplemented :
782
859
return NotImplemented
0 commit comments