Skip to content
11 changes: 11 additions & 0 deletions bigframes/core/compile/ibis_compiler/scalar_op_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from bigframes.core import agg_expressions, ordering
import bigframes.core.compile.ibis_types
import bigframes.core.expression as ex
from bigframes.operations import numeric_ops

if TYPE_CHECKING:
import bigframes.operations as ops
Expand Down Expand Up @@ -267,3 +268,13 @@ def _convert_range_ordering_to_table_value(

# Singleton compiler
scalar_op_compiler = ExpressionCompiler()


@scalar_op_compiler.register_unary_op(numeric_ops.isnan_op)
def isnanornull(arg):
return arg.isnan()


@scalar_op_compiler.register_unary_op(numeric_ops.isfinite_op)
def isfinite(arg):
return arg.isinf().negate() & arg.isnan().negate()
18 changes: 18 additions & 0 deletions bigframes/core/compile/polars/operations/numeric_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,21 @@ def sqrt_op_impl(
import polars as pl

return pl.when(input < 0).then(float("nan")).otherwise(input.sqrt())


@polars_compiler.register_op(numeric_ops.IsNanOp)
def is_nan_op_impl(
compiler: polars_compiler.PolarsExpressionCompiler,
op: numeric_ops.IsNanOp, # type: ignore
input: pl.Expr,
) -> pl.Expr:
return input.is_nan()


@polars_compiler.register_op(numeric_ops.IsFiniteOp)
def is_finite_op_impl(
compiler: polars_compiler.PolarsExpressionCompiler,
op: numeric_ops.IsFiniteOp, # type: ignore
input: pl.Expr,
) -> pl.Expr:
return input.is_finite()
16 changes: 16 additions & 0 deletions bigframes/core/compile/sqlglot/expressions/numeric_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import bigframes.core.compile.sqlglot.expressions.constants as constants
from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr
import bigframes.core.compile.sqlglot.scalar_compiler as scalar_compiler
from bigframes.operations import numeric_ops

register_unary_op = scalar_compiler.scalar_op_compiler.register_unary_op
register_binary_op = scalar_compiler.scalar_op_compiler.register_binary_op
Expand Down Expand Up @@ -408,6 +409,21 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
)


@register_unary_op(numeric_ops.isnan_op)
def isnan(arg: TypedExpr) -> sge.Expression:
return sge.IsNan(this=arg.expr)


@register_unary_op(numeric_ops.isfinite_op)
def isfinite(arg: TypedExpr) -> sge.Expression:
return sge.Not(
this=sge.Or(
this=sge.IsInf(this=arg.expr),
right=sge.IsNan(this=arg.expr),
),
)


def _coerce_bool_to_int(typed_expr: TypedExpr) -> sge.Expression:
"""Coerce boolean expression to integer."""
if typed_expr.dtype == dtypes.BOOL_DTYPE:
Expand Down
16 changes: 16 additions & 0 deletions bigframes/operations/numeric_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,19 @@ def output_type(self, *input_types: dtypes.ExpressionType) -> dtypes.ExpressionT
name="unsafe_pow_op", type_signature=op_typing.BINARY_REAL_NUMERIC
)
unsafe_pow_op = UnsafePowOp()

IsNanOp = base_ops.create_unary_op(
name="isnan",
type_signature=op_typing.FixedOutputType(
dtypes.is_numeric, dtypes.BOOL_DTYPE, "numeric"
),
)
isnan_op = IsNanOp()

IsFiniteOp = base_ops.create_unary_op(
name="isfinite",
type_signature=op_typing.FixedOutputType(
dtypes.is_numeric, dtypes.BOOL_DTYPE, "numeric"
),
)
isfinite_op = IsFiniteOp()
2 changes: 2 additions & 0 deletions bigframes/operations/numpy_op_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
np.ceil: numeric_ops.ceil_op,
np.log1p: numeric_ops.log1p_op,
np.expm1: numeric_ops.expm1_op,
np.isnan: numeric_ops.isnan_op,
np.isfinite: numeric_ops.isfinite_op,
}


Expand Down
2 changes: 2 additions & 0 deletions tests/system/small/test_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
("log10",),
("sqrt",),
("abs",),
("isnan",),
("isfinite",),
],
)
def test_series_ufuncs(floats_pd, floats_bf, opname):
Expand Down