5050from bigframes .core import log_adapter
5151import bigframes .core .block_transforms as block_ops
5252import bigframes .core .blocks as blocks
53+ import bigframes .core .convert
5354import bigframes .core .expression as ex
5455import bigframes .core .groupby as groupby
5556import bigframes .core .guid
@@ -663,22 +664,20 @@ def _apply_binop(
663664 how : str = "outer" ,
664665 reverse : bool = False ,
665666 ):
666- if isinstance (other , (float , int )):
667+ if isinstance (other , (float , int , bool )):
667668 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- )
680669 elif isinstance (other , DataFrame ):
681670 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 )
682681 raise NotImplementedError (
683682 f"binary operation is not implemented on the second operand of type { type (other ).__name__ } ."
684683 f"{ constants .FEEDBACK_LINK } "
@@ -700,22 +699,13 @@ def _apply_scalar_binop(
700699 block = block .drop_columns ([column_id ])
701700 return DataFrame (block )
702701
703- def _apply_series_binop (
702+ def _apply_series_binop_axis_0 (
704703 self ,
705704 other : bigframes .series .Series ,
706705 op : ops .BinaryOp ,
707- axis : str | int = "columns" ,
708706 how : str = "outer" ,
709707 reverse : bool = False ,
710708 ) -> 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-
719709 block , (get_column_left , get_column_right ) = self ._block .join (
720710 other ._block , how = how
721711 )
@@ -738,6 +728,63 @@ def _apply_series_binop(
738728 block = block .with_index_labels (self .index .names )
739729 return DataFrame (block )
740730
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+
741788 def _apply_dataframe_binop (
742789 self ,
743790 other : DataFrame ,
0 commit comments