|
17 | 17 | from typing import TYPE_CHECKING, Any, Hashable, Literal |
18 | 18 |
|
19 | 19 | import bigframes_vendored.pandas.core.col as pd_col |
| 20 | +import numpy |
20 | 21 |
|
21 | 22 | import bigframes.core.expression as bf_expression |
22 | 23 | import bigframes.operations as bf_ops |
@@ -56,14 +57,10 @@ def _apply_binary_op( |
56 | 57 | alignment: Literal["outer", "left"] = "outer", |
57 | 58 | reverse: bool = False, |
58 | 59 | ): |
59 | | - if isinstance(other, Expression): |
60 | | - other_value = other._value |
61 | | - else: |
62 | | - other_value = bf_expression.const(other) |
63 | 60 | if reverse: |
64 | | - return Expression(op.as_expr(other_value, self._value)) |
| 61 | + return Expression(op.as_expr(_as_bf_expr(other), self._value)) |
65 | 62 | else: |
66 | | - return Expression(op.as_expr(self._value, other_value)) |
| 63 | + return Expression(op.as_expr(self._value, _as_bf_expr(other))) |
67 | 64 |
|
68 | 65 | def __add__(self, other: Any) -> Expression: |
69 | 66 | return self._apply_binary_op(other, bf_ops.add_op) |
@@ -164,13 +161,42 @@ def dt(self) -> datetimes.DatetimeSimpleMethods: |
164 | 161 |
|
165 | 162 | return datetimes.DatetimeSimpleMethods(self) |
166 | 163 |
|
| 164 | + def __array_ufunc__( |
| 165 | + self, ufunc: numpy.ufunc, method: str, *inputs, **kwargs |
| 166 | + ) -> Expression: |
| 167 | + """Used to support numpy ufuncs. |
| 168 | + See: https://numpy.org/doc/stable/reference/ufuncs.html |
| 169 | + """ |
| 170 | + # Only __call__ supported with zero arguments |
| 171 | + if method != "__call__" or len(inputs) > 2 or len(kwargs) > 0: |
| 172 | + return NotImplemented |
| 173 | + |
| 174 | + if len(inputs) == 1 and ufunc in bf_ops.NUMPY_TO_OP: |
| 175 | + op = bf_ops.NUMPY_TO_OP[ufunc] |
| 176 | + return Expression(op.as_expr(self._value)) |
| 177 | + if len(inputs) == 2 and ufunc in bf_ops.NUMPY_TO_BINOP: |
| 178 | + binop = bf_ops.NUMPY_TO_BINOP[ufunc] |
| 179 | + if inputs[0] is self: |
| 180 | + return Expression(binop.as_expr(self._value, _as_bf_expr(inputs[1]))) |
| 181 | + else: |
| 182 | + return Expression(binop.as_expr(_as_bf_expr(inputs[0]), self._value)) |
| 183 | + |
| 184 | + return NotImplemented |
| 185 | + |
| 186 | + # keep this last as str declaration can shadow builtins.str |
167 | 187 | @property |
168 | 188 | def str(self) -> strings.StringMethods: |
169 | 189 | import bigframes.operations.strings as strings |
170 | 190 |
|
171 | 191 | return strings.StringMethods(self) |
172 | 192 |
|
173 | 193 |
|
| 194 | +def _as_bf_expr(arg: Any) -> bf_expression.Expression: |
| 195 | + if isinstance(arg, Expression): |
| 196 | + return arg._value |
| 197 | + return bf_expression.const(arg) |
| 198 | + |
| 199 | + |
174 | 200 | def col(col_name: Hashable) -> Expression: |
175 | 201 | return Expression(bf_expression.free_var(col_name)) |
176 | 202 |
|
|
0 commit comments