Skip to content

Commit

Permalink
feat(api): support deferred values in ibis.coalesce/ibis.greatest
Browse files Browse the repository at this point in the history
…/`ibis.least`
  • Loading branch information
jcrist authored and cpcloud committed Sep 28, 2023
1 parent 24ac5e7 commit e423480
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 4 deletions.
80 changes: 77 additions & 3 deletions ibis/expr/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1838,9 +1838,83 @@ def where(cond, true_expr, false_expr) -> ir.Value:
return ifelse(cond, true_expr, false_expr)


coalesce = _deferred(ir.Value.coalesce)
greatest = _deferred(ir.Value.greatest)
least = _deferred(ir.Value.least)
@deferrable
def coalesce(*args: Any) -> ir.Value:
"""Return the first non-null value from `args`.
Parameters
----------
args
Arguments from which to choose the first non-null value
Returns
-------
Value
Coalesced expression
Examples
--------
>>> import ibis
>>> ibis.options.interactive = True
>>> ibis.coalesce(None, 4, 5)
4
"""
if not args:
raise ValueError("Must provide at least one argument")
return ops.Coalesce(args).to_expr()


@deferrable
def greatest(*args: Any) -> ir.Value:
"""Compute the largest value among the supplied arguments.
Parameters
----------
args
Arguments to choose from
Returns
-------
Value
Maximum of the passed arguments
Examples
--------
>>> import ibis
>>> ibis.options.interactive = True
>>> ibis.greatest(None, 4, 5)
5
"""
if not args:
raise ValueError("Must provide at least one argument")
return ops.Greatest(args).to_expr()


@deferrable
def least(*args: Any) -> ir.Value:
"""Compute the smallest value among the supplied arguments.
Parameters
----------
args
Arguments to choose from
Returns
-------
Value
Minimum of the passed arguments
Examples
--------
>>> import ibis
>>> ibis.options.interactive = True
>>> ibis.least(None, 4, 5)
4
"""
if not args:
raise ValueError("Must provide at least one argument")
return ops.Least(args).to_expr()


aggregate = ir.Table.aggregate
cross_join = ir.Table.cross_join
Expand Down
13 changes: 13 additions & 0 deletions ibis/tests/expr/test_sql_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import ibis
import ibis.expr.operations as ops
import ibis.expr.types as ir
from ibis import _
from ibis.tests.util import assert_equal


Expand Down Expand Up @@ -206,3 +207,15 @@ def test_floats(sql_table, function):

expr = function(5.5, 5)
assert isinstance(expr, ir.FloatingScalar)


def test_deferred(sql_table, function):
expr = function(None, _.v3, 2)
res = expr.resolve(sql_table)
sol = function(None, sql_table.v3, 2)
assert res.equals(sol)


def test_no_arguments_errors(function):
with pytest.raises(ValueError, match="at least one argument"):
function()
2 changes: 1 addition & 1 deletion ibis/tests/expr/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -1861,7 +1861,7 @@ def test_invalid_deferred():
t = ibis.table(dict(value="int", lagged_value="int"), name="t")

with pytest.raises(ValidationError):
ibis.greatest(t.value, ibis._.lagged_value)
ops.Greatest((t.value, ibis._.lagged_value))


@pytest.mark.parametrize("keep", ["last", None])
Expand Down

0 comments on commit e423480

Please sign in to comment.