From 94bd4df81d73f8c894eb5b72e747b7a79cdf14f6 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Fri, 9 Sep 2022 12:15:27 -0500 Subject: [PATCH] feat(api): add `and_` and `or_` helpers --- docs/api/expressions/top_level.md | 2 ++ ibis/expr/api.py | 43 ++++++++++++++++++++++++++++- ibis/tests/expr/test_value_exprs.py | 20 ++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/docs/api/expressions/top_level.md b/docs/api/expressions/top_level.md index f352a37a6443..fea9d63583d1 100644 --- a/docs/api/expressions/top_level.md +++ b/docs/api/expressions/top_level.md @@ -6,6 +6,7 @@ These methods and objects are available directly in the `ibis` module. `NA` is the null scalar. +::: ibis.and* ::: ibis.array ::: ibis.asc ::: ibis.case @@ -24,6 +25,7 @@ These methods and objects are available directly in the `ibis` module. ::: ibis.negate ::: ibis.now ::: ibis.null +::: ibis.or* ::: ibis.param ::: ibis.show_sql ::: ibis.random diff --git a/ibis/expr/api.py b/ibis/expr/api.py index dbdc6e09c517..3a744cc5646d 100644 --- a/ibis/expr/api.py +++ b/ibis/expr/api.py @@ -5,6 +5,7 @@ import datetime import functools import itertools +import operator from typing import Iterable, Mapping, Sequence from typing import Tuple as _Tuple from typing import TypeVar @@ -115,7 +116,9 @@ __all__ = ( 'aggregate', + 'and_', 'array', + 'asc', 'case', 'coalesce', 'connect', @@ -124,7 +127,6 @@ 'date', 'desc', 'difference', - 'asc', 'e', 'Expr', 'geo_area', @@ -194,6 +196,7 @@ 'negate', 'now', 'null', + 'or_', 'param', 'pi', 'prevent_rewrite', @@ -551,6 +554,44 @@ def asc(expr: ir.Column | str) -> ir.SortExpr | ops.DeferredSortKey: return ops.SortKey(expr).to_expr() +def and_(*predicates: ir.BooleanValue) -> ir.BooleanValue: + """Combine multiple predicates using `&`. + + Parameters + ---------- + predicates + Boolean value expressions + + Returns + ------- + BooleanValue + A new predicate that evaluates to True if all composing predicates are + True. If no predicates were provided, returns True. + """ + if not predicates: + return literal(True) + return functools.reduce(operator.and_, predicates) + + +def or_(*predicates: ir.BooleanValue) -> ir.BooleanValue: + """Combine multiple predicates using `|`. + + Parameters + ---------- + predicates + Boolean value expressions + + Returns + ------- + BooleanValue + A new predicate that evaluates to True if any composing predicates are + True. If no predicates were provided, returns False. + """ + if not predicates: + return literal(False) + return functools.reduce(operator.or_, predicates) + + @functools.singledispatch def timestamp( value, diff --git a/ibis/tests/expr/test_value_exprs.py b/ibis/tests/expr/test_value_exprs.py index f151c013e08a..a925429f5a80 100644 --- a/ibis/tests/expr/test_value_exprs.py +++ b/ibis/tests/expr/test_value_exprs.py @@ -600,6 +600,26 @@ def test_boolean_logical_ops(table, operation): assert isinstance(result, ir.BooleanScalar) +def test_and_(table): + p1 = table.a > 1 + p2 = table.b > 1 + p3 = table.c > 1 + assert ibis.and_().equals(ibis.literal(True)) + assert ibis.and_(p1).equals(p1) + assert ibis.and_(p1, p2).equals(p1 & p2) + assert ibis.and_(p1, p2, p3).equals(p1 & p2 & p3) + + +def test_or_(table): + p1 = table.a > 1 + p2 = table.b > 1 + p3 = table.c > 1 + assert ibis.or_().equals(ibis.literal(False)) + assert ibis.or_(p1).equals(p1) + assert ibis.or_(p1, p2).equals(p1 | p2) + assert ibis.or_(p1, p2, p3).equals(p1 | p2 | p3) + + def test_null_column(): t = ibis.table([('a', 'string')], name='t') s = t.mutate(b=ibis.NA)