50
50
from bigframes .core import log_adapter
51
51
import bigframes .core .block_transforms as block_ops
52
52
import bigframes .core .blocks as blocks
53
+ import bigframes .core .convert
53
54
import bigframes .core .expression as ex
54
55
import bigframes .core .groupby as groupby
55
56
import bigframes .core .guid
@@ -663,22 +664,20 @@ def _apply_binop(
663
664
how : str = "outer" ,
664
665
reverse : bool = False ,
665
666
):
666
- if isinstance (other , (float , int )):
667
+ if isinstance (other , (float , int , bool )):
667
668
return self ._apply_scalar_binop (other , op , reverse = reverse )
668
- elif isinstance (other , indexes .Index ):
669
- return self ._apply_series_binop (
670
- other .to_series (index = self .index ),
671
- op ,
672
- axis = axis ,
673
- how = how ,
674
- reverse = reverse ,
675
- )
676
- elif isinstance (other , bigframes .series .Series ):
677
- return self ._apply_series_binop (
678
- other , op , axis = axis , how = how , reverse = reverse
679
- )
680
669
elif isinstance (other , DataFrame ):
681
670
return self ._apply_dataframe_binop (other , op , how = how , reverse = reverse )
671
+ elif isinstance (other , pandas .DataFrame ):
672
+ return self ._apply_dataframe_binop (
673
+ DataFrame (other ), op , how = how , reverse = reverse
674
+ )
675
+ elif utils .get_axis_number (axis ) == 0 :
676
+ bf_series = bigframes .core .convert .to_bf_series (other , self .index )
677
+ return self ._apply_series_binop_axis_0 (bf_series , op , how , reverse )
678
+ elif utils .get_axis_number (axis ) == 1 :
679
+ pd_series = bigframes .core .convert .to_pd_series (other , self .columns )
680
+ return self ._apply_series_binop_axis_1 (pd_series , op , how , reverse )
682
681
raise NotImplementedError (
683
682
f"binary operation is not implemented on the second operand of type { type (other ).__name__ } ."
684
683
f"{ constants .FEEDBACK_LINK } "
@@ -700,22 +699,13 @@ def _apply_scalar_binop(
700
699
block = block .drop_columns ([column_id ])
701
700
return DataFrame (block )
702
701
703
- def _apply_series_binop (
702
+ def _apply_series_binop_axis_0 (
704
703
self ,
705
704
other : bigframes .series .Series ,
706
705
op : ops .BinaryOp ,
707
- axis : str | int = "columns" ,
708
706
how : str = "outer" ,
709
707
reverse : bool = False ,
710
708
) -> DataFrame :
711
- if axis not in ("columns" , "index" , 0 , 1 ):
712
- raise ValueError (f"Invalid input: axis { axis } ." )
713
-
714
- if axis in ("columns" , 1 ):
715
- raise NotImplementedError (
716
- f"Row Series operations haven't been supported. { constants .FEEDBACK_LINK } "
717
- )
718
-
719
709
block , (get_column_left , get_column_right ) = self ._block .join (
720
710
other ._block , how = how
721
711
)
@@ -738,6 +728,63 @@ def _apply_series_binop(
738
728
block = block .with_index_labels (self .index .names )
739
729
return DataFrame (block )
740
730
731
+ def _apply_series_binop_axis_1 (
732
+ self ,
733
+ other : pandas .Series ,
734
+ op : ops .BinaryOp ,
735
+ how : str = "outer" ,
736
+ reverse : bool = False ,
737
+ ) -> DataFrame :
738
+ # Somewhat different alignment than df-df so separate codepath for now.
739
+ if self .columns .equals (other .index ):
740
+ columns , lcol_indexer , rcol_indexer = self .columns , None , None
741
+ else :
742
+ if not (self .columns .is_unique and other .index .is_unique ):
743
+ raise ValueError ("Cannot align non-unique indices" )
744
+ columns , lcol_indexer , rcol_indexer = self .columns .join (
745
+ other .index , how = how , return_indexers = True
746
+ )
747
+
748
+ binop_result_ids = []
749
+
750
+ column_indices = zip (
751
+ lcol_indexer if (lcol_indexer is not None ) else range (len (columns )),
752
+ rcol_indexer if (rcol_indexer is not None ) else range (len (columns )),
753
+ )
754
+
755
+ block = self ._block
756
+ for left_index , right_index in column_indices :
757
+ if left_index >= 0 and right_index >= 0 : # -1 indices indicate missing
758
+ self_col_id = self ._block .value_columns [left_index ]
759
+ other_scalar = other .iloc [right_index ]
760
+ expr = (
761
+ op .as_expr (ex .const (other_scalar ), self_col_id )
762
+ if reverse
763
+ else op .as_expr (self_col_id , ex .const (other_scalar ))
764
+ )
765
+ elif left_index >= 0 :
766
+ self_col_id = self ._block .value_columns [left_index ]
767
+ expr = (
768
+ op .as_expr (ex .const (None ), self_col_id )
769
+ if reverse
770
+ else op .as_expr (self_col_id , ex .const (None ))
771
+ )
772
+ elif right_index >= 0 :
773
+ other_scalar = other .iloc [right_index ]
774
+ expr = (
775
+ op .as_expr (ex .const (other_scalar ), ex .const (None ))
776
+ if reverse
777
+ else op .as_expr (ex .const (None ), ex .const (other_scalar ))
778
+ )
779
+ else :
780
+ # Should not be possible
781
+ raise ValueError ("No right or left index." )
782
+ block , result_col_id = block .project_expr (expr )
783
+ binop_result_ids .append (result_col_id )
784
+
785
+ block = block .select_columns (binop_result_ids )
786
+ return DataFrame (block .with_column_labels (columns ))
787
+
741
788
def _apply_dataframe_binop (
742
789
self ,
743
790
other : DataFrame ,
0 commit comments