2
2
Base and utility classes for tseries type pandas objects.
3
3
"""
4
4
from datetime import datetime
5
- from typing import Any , List , Optional , Union , cast
5
+ from typing import Any , List , Optional , TypeVar , Union , cast
6
6
7
7
import numpy as np
8
8
45
45
46
46
_index_doc_kwargs = dict (ibase ._index_doc_kwargs )
47
47
48
+ _T = TypeVar ("_T" , bound = "DatetimeIndexOpsMixin" )
49
+
48
50
49
51
def _join_i8_wrapper (joinf , with_indexers : bool = True ):
50
52
"""
@@ -655,13 +657,7 @@ def intersection(self, other, sort=False):
655
657
result = result ._with_freq ("infer" )
656
658
return result
657
659
658
- elif (
659
- other .freq is None
660
- or self .freq is None
661
- or other .freq != self .freq
662
- or not other .freq .is_anchored ()
663
- or (not self .is_monotonic or not other .is_monotonic )
664
- ):
660
+ elif not self ._can_fast_intersect (other ):
665
661
result = Index .intersection (self , other , sort = sort )
666
662
result = result ._with_freq ("infer" )
667
663
return result
@@ -684,7 +680,28 @@ def intersection(self, other, sort=False):
684
680
left_chunk = left ._values [lslice ]
685
681
return self ._shallow_copy (left_chunk )
686
682
687
- def _can_fast_union (self , other ) -> bool :
683
+ def _can_fast_intersect (self : _T , other : _T ) -> bool :
684
+ if self .freq is None :
685
+ return False
686
+
687
+ if other .freq != self .freq :
688
+ return False
689
+
690
+ if not self .is_monotonic_increasing :
691
+ # Because freq is not None, we must then be monotonic decreasing
692
+ return False
693
+
694
+ if not self .freq .is_anchored ():
695
+ # If freq is not anchored, then despite having matching freqs,
696
+ # we might not "line up"
697
+ return False
698
+
699
+ return True
700
+
701
+ def _can_fast_union (self : _T , other : _T ) -> bool :
702
+ # Assumes that type(self) == type(other), as per the annotation
703
+ # The ability to fast_union also implies that `freq` should be
704
+ # retained on union.
688
705
if not isinstance (other , type (self )):
689
706
return False
690
707
@@ -693,7 +710,9 @@ def _can_fast_union(self, other) -> bool:
693
710
if freq is None or freq != other .freq :
694
711
return False
695
712
696
- if not self .is_monotonic or not other .is_monotonic :
713
+ if not self .is_monotonic_increasing :
714
+ # Because freq is not None, we must then be monotonic decreasing
715
+ # TODO: do union on the reversed indexes?
697
716
return False
698
717
699
718
if len (self ) == 0 or len (other ) == 0 :
@@ -709,12 +728,7 @@ def _can_fast_union(self, other) -> bool:
709
728
left_end = left [- 1 ]
710
729
711
730
# Only need to "adjoin", not overlap
712
- try :
713
- return (right_start == left_end + freq ) or right_start in left
714
- except ValueError :
715
- # if we are comparing a freq that does not propagate timezones
716
- # this will raise
717
- return False
731
+ return (right_start == left_end + freq ) or right_start in left
718
732
719
733
def _fast_union (self , other , sort = None ):
720
734
if len (other ) == 0 :
@@ -734,7 +748,7 @@ def _fast_union(self, other, sort=None):
734
748
loc = right .searchsorted (left_start , side = "left" )
735
749
right_chunk = right ._values [:loc ]
736
750
dates = concat_compat ((left ._values , right_chunk ))
737
- # TODO: can we infer that it has self.freq?
751
+ # With sort being False, we can't infer that result.freq == self.freq
738
752
result = self ._shallow_copy (dates )._with_freq ("infer" )
739
753
return result
740
754
else :
@@ -748,8 +762,10 @@ def _fast_union(self, other, sort=None):
748
762
loc = right .searchsorted (left_end , side = "right" )
749
763
right_chunk = right ._values [loc :]
750
764
dates = concat_compat ([left ._values , right_chunk ])
751
- # TODO: can we infer that it has self.freq?
752
- result = self ._shallow_copy (dates )._with_freq ("infer" )
765
+ # The can_fast_union check ensures that the result.freq
766
+ # should match self.freq
767
+ dates = type (self ._data )(dates , freq = self .freq )
768
+ result = type (self )._simple_new (dates , name = self .name )
753
769
return result
754
770
else :
755
771
return left
@@ -766,6 +782,8 @@ def _union(self, other, sort):
766
782
if this ._can_fast_union (other ):
767
783
result = this ._fast_union (other , sort = sort )
768
784
if result .freq is None :
785
+ # In the case where sort is None, _can_fast_union
786
+ # implies that result.freq should match self.freq
769
787
result = result ._with_freq ("infer" )
770
788
return result
771
789
else :
0 commit comments