3333)
3434import pandas .core .indexes .base as ibase
3535from pandas .core .indexes .base import Index , _index_shared_docs
36+ from pandas .core .indexes .numeric import Int64Index
3637from pandas .core .tools .timedeltas import to_timedelta
3738
3839from pandas .tseries .frequencies import DateOffset , to_offset
@@ -71,36 +72,6 @@ def method(self, other):
7172 return method
7273
7374
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-
10475class DatetimeIndexOpsMixin (ExtensionOpsMixin ):
10576 """
10677 Common ops mixin to support a unified interface datetimelike Index.
@@ -125,6 +96,10 @@ class DatetimeIndexOpsMixin(ExtensionOpsMixin):
12596 __iter__ = ea_passthrough (DatetimeLikeArrayMixin .__iter__ )
12697 mean = ea_passthrough (DatetimeLikeArrayMixin .mean )
12798
99+ @property
100+ def is_all_dates (self ) -> bool :
101+ return True
102+
128103 @property
129104 def freq (self ):
130105 """
@@ -605,66 +580,6 @@ def isin(self, values, level=None):
605580
606581 return algorithms .isin (self .asi8 , values .asi8 )
607582
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-
668583 @Appender (_index_shared_docs ["repeat" ] % _index_doc_kwargs )
669584 def repeat (self , repeats , axis = None ):
670585 nv .validate_repeat (tuple (), dict (axis = axis ))
@@ -777,6 +692,168 @@ def shift(self, periods=1, freq=None):
777692 return type (self )(result , name = self .name )
778693
779694
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+
780857def wrap_arithmetic_op (self , other , result ):
781858 if result is NotImplemented :
782859 return NotImplemented
0 commit comments