@@ -2065,41 +2065,32 @@ def apply(
20652065 " are supported."
20662066 )
20672067
2068- from bigframes ._config import options
20692068
2069+ # Highest priority: try to map directly to an operator, for eg numpy
2070+ # ufuncs, or simple arithmetic/logic operators.
20702071 bf_op = python_ops .python_callable_to_op (func )
20712072 if bf_op and isinstance (bf_op , ops .UnaryOp ):
20722073 return self ._apply_unary_op (bf_op )
20732074
2074- if isinstance (func , bigframes .functions .Udf ) or (
2075- options .experiments .enable_python_transpiler and callable (func )
2076- ):
2077- # We are working with bigquery function at this point
2078- result_series = self ._apply_callable_expr (ops .func_to_expr (func ), args )
2079- # TODO(jialuo): Investigate why `_apply_nary_op` drops the series
2080- # `name`. Manually reassigning it here as a temporary fix.
2081- result_series .name = self .name
2082-
2083- return result_series
2084-
2085- # It is neither a remote function nor a managed function.
2086- # Then it must be a vectorized function that applies to the Series
2087- # as a whole.
2088- if by_row :
2089- raise ValueError (
2090- "You have passed a function as-is. If your intention is to "
2091- "apply this function in a vectorized way (i.e. to the "
2092- "entire Series as a whole, and you are sure that it "
2093- "performs only the operations that are implemented for a "
2094- "Series (e.g. a chain of arithmetic/logical operations, "
2095- "such as `def foo(s): return s % 2 == 1`), please also "
2096- "specify `by_row=False`. If your function contains "
2097- "arbitrary code, it can only be applied to every element "
2098- "in the Series individually, in which case you must "
2099- "convert it to a BigFrames BigQuery function using "
2100- "`bigframes.pandas.udf`, "
2101- "or `bigframes.pandas.remote_function` before passing."
2102- )
2075+ # "compat": try by row, and then fall back to passing whole series if that fails
2076+ if by_row :
2077+ try :
2078+ return self ._apply_by_row (func )
2079+ except Exception as ex :
2080+ raise ValueError (
2081+ "You have passed a function as-is. If your intention is to "
2082+ "apply this function in a vectorized way (i.e. to the "
2083+ "entire Series as a whole, and you are sure that it "
2084+ "performs only the operations that are implemented for a "
2085+ "Series (e.g. a chain of arithmetic/logical operations, "
2086+ "such as `def foo(s): return s % 2 == 1`), please also "
2087+ "specify `by_row=False`. If your function contains "
2088+ "arbitrary code, it can only be applied to every element "
2089+ "in the Series individually, in which case you must "
2090+ "convert it to a BigFrames BigQuery function using "
2091+ "`bigframes.pandas.udf`, "
2092+ "or `bigframes.pandas.remote_function` before passing."
2093+ )
21032094
21042095 try :
21052096 return func (self ) # type: ignore
@@ -2111,6 +2102,21 @@ def apply(
21112102 ex .message += f"\n { _bigquery_function_recommendation_message } "
21122103 raise
21132104
2105+ def _apply_by_row (self , func : typing .Callable , args : typing .Tuple = ()) -> Series :
2106+ from bigframes ._config import options
2107+
2108+ if isinstance (func , bigframes .functions .Udf ) or (
2109+ options .experiments .enable_python_transpiler and callable (func )
2110+ ):
2111+ # We are working with bigquery function at this point
2112+ result_series = self ._apply_callable_expr (ops .func_to_expr (func ), args )
2113+ # TODO(jialuo): Investigate why `_apply_nary_op` drops the series
2114+ # `name`. Manually reassigning it here as a temporary fix.
2115+ result_series .name = self .name
2116+
2117+ return result_series
2118+ raise ValueError (f"Cannot apply function { func } to Series { self } " )
2119+
21142120 def combine (
21152121 self ,
21162122 other ,
0 commit comments