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 ))
@@ -778,6 +693,168 @@ def shift(self, periods, freq=None):
778
693
return type (self )(result , name = self .name )
779
694
780
695
696
+ class DatetimeTimedeltaMixin (DatetimeIndexOpsMixin , Int64Index ):
697
+ """
698
+ Mixin class for methods shared by DatetimeIndex and TimedeltaIndex,
699
+ but not PeriodIndex
700
+ """
701
+
702
+ # Compat for frequency inference, see GH#23789
703
+ _is_monotonic_increasing = Index .is_monotonic_increasing
704
+ _is_monotonic_decreasing = Index .is_monotonic_decreasing
705
+ _is_unique = Index .is_unique
706
+
707
+ def _set_freq (self , freq ):
708
+ """
709
+ Set the _freq attribute on our underlying DatetimeArray.
710
+
711
+ Parameters
712
+ ----------
713
+ freq : DateOffset, None, or "infer"
714
+ """
715
+ # GH#29843
716
+ if freq is None :
717
+ # Always valid
718
+ pass
719
+ elif len (self ) == 0 and isinstance (freq , DateOffset ):
720
+ # Always valid. In the TimedeltaIndex case, we assume this
721
+ # is a Tick offset.
722
+ pass
723
+ else :
724
+ # As an internal method, we can ensure this assertion always holds
725
+ assert freq == "infer"
726
+ freq = to_offset (self .inferred_freq )
727
+
728
+ self ._data ._freq = freq
729
+
730
+ # --------------------------------------------------------------------
731
+ # Set Operation Methods
732
+
733
+ @Appender (Index .difference .__doc__ )
734
+ def difference (self , other , sort = None ):
735
+ new_idx = super ().difference (other , sort = sort )
736
+ new_idx ._set_freq (None )
737
+ return new_idx
738
+
739
+ def intersection (self , other , sort = False ):
740
+ """
741
+ Specialized intersection for DatetimeIndex/TimedeltaIndex.
742
+
743
+ May be much faster than Index.intersection
744
+
745
+ Parameters
746
+ ----------
747
+ other : Same type as self or array-like
748
+ sort : False or None, default False
749
+ Sort the resulting index if possible.
750
+
751
+ .. versionadded:: 0.24.0
752
+
753
+ .. versionchanged:: 0.24.1
754
+
755
+ Changed the default to ``False`` to match the behaviour
756
+ from before 0.24.0.
757
+
758
+ .. versionchanged:: 0.25.0
759
+
760
+ The `sort` keyword is added
761
+
762
+ Returns
763
+ -------
764
+ y : Index or same type as self
765
+ """
766
+ self ._validate_sort_keyword (sort )
767
+ self ._assert_can_do_setop (other )
768
+
769
+ if self .equals (other ):
770
+ return self ._get_reconciled_name_object (other )
771
+
772
+ if len (self ) == 0 :
773
+ return self .copy ()
774
+ if len (other ) == 0 :
775
+ return other .copy ()
776
+
777
+ if not isinstance (other , type (self )):
778
+ result = Index .intersection (self , other , sort = sort )
779
+ if isinstance (result , type (self )):
780
+ if result .freq is None :
781
+ result ._set_freq ("infer" )
782
+ return result
783
+
784
+ elif (
785
+ other .freq is None
786
+ or self .freq is None
787
+ or other .freq != self .freq
788
+ or not other .freq .is_anchored ()
789
+ or (not self .is_monotonic or not other .is_monotonic )
790
+ ):
791
+ result = Index .intersection (self , other , sort = sort )
792
+
793
+ # Invalidate the freq of `result`, which may not be correct at
794
+ # this point, depending on the values.
795
+
796
+ result ._set_freq (None )
797
+ if hasattr (self , "tz" ):
798
+ result = self ._shallow_copy (
799
+ result ._values , name = result .name , tz = result .tz , freq = None
800
+ )
801
+ else :
802
+ result = self ._shallow_copy (result ._values , name = result .name , freq = None )
803
+ if result .freq is None :
804
+ result ._set_freq ("infer" )
805
+ return result
806
+
807
+ # to make our life easier, "sort" the two ranges
808
+ if self [0 ] <= other [0 ]:
809
+ left , right = self , other
810
+ else :
811
+ left , right = other , self
812
+
813
+ # after sorting, the intersection always starts with the right index
814
+ # and ends with the index of which the last elements is smallest
815
+ end = min (left [- 1 ], right [- 1 ])
816
+ start = right [0 ]
817
+
818
+ if end < start :
819
+ return type (self )(data = [])
820
+ else :
821
+ lslice = slice (* left .slice_locs (start , end ))
822
+ left_chunk = left .values [lslice ]
823
+ return self ._shallow_copy (left_chunk )
824
+
825
+ def _can_fast_union (self , other ) -> bool :
826
+ if not isinstance (other , type (self )):
827
+ return False
828
+
829
+ freq = self .freq
830
+
831
+ if freq is None or freq != other .freq :
832
+ return False
833
+
834
+ if not self .is_monotonic or not other .is_monotonic :
835
+ return False
836
+
837
+ if len (self ) == 0 or len (other ) == 0 :
838
+ return True
839
+
840
+ # to make our life easier, "sort" the two ranges
841
+ if self [0 ] <= other [0 ]:
842
+ left , right = self , other
843
+ else :
844
+ left , right = other , self
845
+
846
+ right_start = right [0 ]
847
+ left_end = left [- 1 ]
848
+
849
+ # Only need to "adjoin", not overlap
850
+ try :
851
+ return (right_start == left_end + freq ) or right_start in left
852
+ except ValueError :
853
+ # if we are comparing a freq that does not propagate timezones
854
+ # this will raise
855
+ return False
856
+
857
+
781
858
def wrap_arithmetic_op (self , other , result ):
782
859
if result is NotImplemented :
783
860
return NotImplemented
0 commit comments