5
5
"""
6
6
import datetime
7
7
import operator
8
- from typing import Set , Tuple , Union
8
+ from typing import Optional , Set , Tuple , Union
9
9
10
10
import numpy as np
11
11
12
12
from pandas ._libs import Timedelta , Timestamp , lib
13
13
from pandas ._libs .ops_dispatch import maybe_dispatch_ufunc_to_dunder_op # noqa:F401
14
+ from pandas ._typing import Level
14
15
from pandas .util ._decorators import Appender
15
16
16
17
from pandas .core .dtypes .common import is_list_like , is_timedelta64_dtype
@@ -615,8 +616,27 @@ def _combine_series_frame(left, right, func, axis: int):
615
616
return left ._construct_result (new_data )
616
617
617
618
618
- def _align_method_FRAME (left , right , axis ):
619
- """ convert rhs to meet lhs dims if input is list, tuple or np.ndarray """
619
+ def _align_method_FRAME (
620
+ left , right , axis , flex : Optional [bool ] = False , level : Level = None
621
+ ):
622
+ """
623
+ Convert rhs to meet lhs dims if input is list, tuple or np.ndarray.
624
+
625
+ Parameters
626
+ ----------
627
+ left : DataFrame
628
+ right : Any
629
+ axis: int, str, or None
630
+ flex: bool or None, default False
631
+ Whether this is a flex op, in which case we reindex.
632
+ None indicates not to check for alignment.
633
+ level : int or level name, default None
634
+
635
+ Returns
636
+ -------
637
+ left : DataFrame
638
+ right : Any
639
+ """
620
640
621
641
def to_series (right ):
622
642
msg = "Unable to coerce to Series, length must be {req_len}: given {given_len}"
@@ -667,7 +687,22 @@ def to_series(right):
667
687
# GH17901
668
688
right = to_series (right )
669
689
670
- return right
690
+ if flex is not None and isinstance (right , ABCDataFrame ):
691
+ if not left ._indexed_same (right ):
692
+ if flex :
693
+ left , right = left .align (right , join = "outer" , level = level , copy = False )
694
+ else :
695
+ raise ValueError (
696
+ "Can only compare identically-labeled DataFrame objects"
697
+ )
698
+ elif isinstance (right , ABCSeries ):
699
+ # axis=1 is default for DataFrame-with-Series op
700
+ axis = left ._get_axis_number (axis ) if axis is not None else 1
701
+ left , right = left .align (
702
+ right , join = "outer" , axis = axis , level = level , copy = False
703
+ )
704
+
705
+ return left , right
671
706
672
707
673
708
def _arith_method_FRAME (cls , op , special ):
@@ -687,16 +722,15 @@ def _arith_method_FRAME(cls, op, special):
687
722
@Appender (doc )
688
723
def f (self , other , axis = default_axis , level = None , fill_value = None ):
689
724
690
- other = _align_method_FRAME (self , other , axis )
725
+ self , other = _align_method_FRAME (self , other , axis , flex = True , level = level )
691
726
692
727
if isinstance (other , ABCDataFrame ):
693
728
# Another DataFrame
694
729
pass_op = op if should_series_dispatch (self , other , op ) else na_op
695
730
pass_op = pass_op if not is_logical else op
696
731
697
- left , right = self .align (other , join = "outer" , level = level , copy = False )
698
- new_data = left ._combine_frame (right , pass_op , fill_value )
699
- return left ._construct_result (new_data )
732
+ new_data = self ._combine_frame (other , pass_op , fill_value )
733
+ return self ._construct_result (new_data )
700
734
701
735
elif isinstance (other , ABCSeries ):
702
736
# For these values of `axis`, we end up dispatching to Series op,
@@ -708,9 +742,6 @@ def f(self, other, axis=default_axis, level=None, fill_value=None):
708
742
raise NotImplementedError (f"fill_value { fill_value } not supported." )
709
743
710
744
axis = self ._get_axis_number (axis ) if axis is not None else 1
711
- self , other = self .align (
712
- other , join = "outer" , axis = axis , level = level , copy = False
713
- )
714
745
return _combine_series_frame (self , other , pass_op , axis = axis )
715
746
else :
716
747
# in this case we always have `np.ndim(other) == 0`
@@ -737,20 +768,15 @@ def _flex_comp_method_FRAME(cls, op, special):
737
768
@Appender (doc )
738
769
def f (self , other , axis = default_axis , level = None ):
739
770
740
- other = _align_method_FRAME (self , other , axis )
771
+ self , other = _align_method_FRAME (self , other , axis , flex = True , level = level )
741
772
742
773
if isinstance (other , ABCDataFrame ):
743
774
# Another DataFrame
744
- if not self ._indexed_same (other ):
745
- self , other = self .align (other , "outer" , level = level , copy = False )
746
775
new_data = dispatch_to_series (self , other , op , str_rep )
747
776
return self ._construct_result (new_data )
748
777
749
778
elif isinstance (other , ABCSeries ):
750
779
axis = self ._get_axis_number (axis ) if axis is not None else 1
751
- self , other = self .align (
752
- other , join = "outer" , axis = axis , level = level , copy = False
753
- )
754
780
return _combine_series_frame (self , other , op , axis = axis )
755
781
else :
756
782
# in this case we always have `np.ndim(other) == 0`
@@ -769,21 +795,15 @@ def _comp_method_FRAME(cls, op, special):
769
795
@Appender (f"Wrapper for comparison method { op_name } " )
770
796
def f (self , other ):
771
797
772
- other = _align_method_FRAME (self , other , axis = None )
798
+ self , other = _align_method_FRAME (
799
+ self , other , axis = None , level = None , flex = False
800
+ )
773
801
774
802
if isinstance (other , ABCDataFrame ):
775
803
# Another DataFrame
776
- if not self ._indexed_same (other ):
777
- raise ValueError (
778
- "Can only compare identically-labeled DataFrame objects"
779
- )
780
804
new_data = dispatch_to_series (self , other , op , str_rep )
781
805
782
806
elif isinstance (other , ABCSeries ):
783
- # axis=1 is default for DataFrame-with-Series op
784
- self , other = self .align (
785
- other , join = "outer" , axis = 1 , level = None , copy = False
786
- )
787
807
new_data = dispatch_to_series (self , other , op , axis = "columns" )
788
808
789
809
else :
0 commit comments