Skip to content

Commit 54889db

Browse files
authored
feat(api): move from .case() to .cases() (#9096)
1 parent 22dcce1 commit 54889db

File tree

26 files changed

+384
-481
lines changed

26 files changed

+384
-481
lines changed

docs/_freeze/posts/ci-analysis/index/execute-results/html.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/_quarto.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ quartodoc:
332332
- name: ifelse
333333
dynamic: true
334334
signature_name: full
335-
- name: case
335+
- name: cases
336336
dynamic: true
337337
signature_name: full
338338

docs/posts/ci-analysis/index.qmd

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,11 @@ Let's also give them some names that'll look nice on our plots.
203203
stats = stats.mutate(
204204
raw_improvements=_.has_poetry.cast("int") + _.has_team.cast("int")
205205
).mutate(
206-
improvements=(
207-
_.raw_improvements.case()
208-
.when(0, "None")
209-
.when(1, "Poetry")
210-
.when(2, "Poetry + Team Plan")
211-
.else_("NA")
212-
.end()
206+
improvements=_.raw_improvements.cases(
207+
(0, "None"),
208+
(1, "Poetry"),
209+
(2, "Poetry + Team Plan"),
210+
else_="NA",
213211
),
214212
team_plan=ibis.ifelse(_.raw_improvements > 1, "Poetry + Team Plan", "None"),
215213
)

docs/tutorials/ibis-for-sql-users.qmd

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -466,11 +466,11 @@ semantics:
466466
case = (
467467
t.one.cast("timestamp")
468468
.year()
469-
.case()
470-
.when(2015, "This year")
471-
.when(2014, "Last year")
472-
.else_("Earlier")
473-
.end()
469+
.cases(
470+
(2015, "This year"),
471+
(2014, "Last year"),
472+
else_="Earlier",
473+
)
474474
)
475475
476476
expr = t.mutate(year_group=case)
@@ -489,18 +489,16 @@ CASE
489489
END
490490
```
491491

492-
To do this, use `ibis.case`:
492+
To do this, use `ibis.cases`:
493493

494494
```{python}
495-
case = (
496-
ibis.case()
497-
.when(t.two < 0, t.three * 2)
498-
.when(t.two > 1, t.three)
499-
.else_(t.two)
500-
.end()
495+
cases = ibis.cases(
496+
(t.two < 0, t.three * 2),
497+
(t.two > 1, t.three),
498+
else_=t.two,
501499
)
502500
503-
expr = t.mutate(cond_value=case)
501+
expr = t.mutate(cond_value=cases)
504502
ibis.to_sql(expr)
505503
```
506504

ibis/backends/clickhouse/tests/test_operators.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,22 +201,18 @@ def test_ifelse(alltypes, df, op, pandas_op):
201201

202202
def test_simple_case(con, alltypes, assert_sql):
203203
t = alltypes
204-
expr = (
205-
t.string_col.case().when("foo", "bar").when("baz", "qux").else_("default").end()
206-
)
204+
expr = t.string_col.cases(("foo", "bar"), ("baz", "qux"), else_="default")
207205

208206
assert_sql(expr)
209207
assert len(con.execute(expr))
210208

211209

212210
def test_search_case(con, alltypes, assert_sql):
213211
t = alltypes
214-
expr = (
215-
ibis.case()
216-
.when(t.float_col > 0, t.int_col * 2)
217-
.when(t.float_col < 0, t.int_col)
218-
.else_(0)
219-
.end()
212+
expr = ibis.cases(
213+
(t.float_col > 0, t.int_col * 2),
214+
(t.float_col < 0, t.int_col),
215+
else_=0,
220216
)
221217

222218
assert_sql(expr)

ibis/backends/impala/tests/test_case_exprs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ def table(mockcon):
1414

1515
@pytest.fixture
1616
def simple_case(table):
17-
return table.g.case().when("foo", "bar").when("baz", "qux").else_("default").end()
17+
return table.g.cases(("foo", "bar"), ("baz", "qux"), else_="default")
1818

1919

2020
@pytest.fixture
2121
def search_case(table):
2222
t = table
23-
return ibis.case().when(t.f > 0, t.d * 2).when(t.c < 0, t.a * 2).end()
23+
return ibis.cases((t.f > 0, t.d * 2), (t.c < 0, t.a * 2))
2424

2525

2626
@pytest.fixture

ibis/backends/snowflake/tests/test_udf.py

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import pytest
99
from pytest import param
1010

11-
import ibis
1211
import ibis.expr.datatypes as dt
1312
from ibis import udf
1413

@@ -122,36 +121,23 @@ def predict_price(
122121
df.columns = ["CARAT_SCALED", "CUT_ENCODED", "COLOR_ENCODED", "CLARITY_ENCODED"]
123122
return model.predict(df)
124123

125-
def cases(value, mapping):
126-
"""This should really be a top-level function or method."""
127-
expr = ibis.case()
128-
for k, v in mapping.items():
129-
expr = expr.when(value == k, v)
130-
return expr.end()
131-
132124
diamonds = con.tables.DIAMONDS
133125
expr = diamonds.mutate(
134126
predicted_price=predict_price(
135127
(_.carat - _.carat.mean()) / _.carat.std(),
136-
cases(
137-
_.cut,
138-
{
139-
c: i
140-
for i, c in enumerate(
141-
("Fair", "Good", "Very Good", "Premium", "Ideal"), start=1
142-
)
143-
},
128+
_.cut.cases(
129+
(c, i)
130+
for i, c in enumerate(
131+
("Fair", "Good", "Very Good", "Premium", "Ideal"), start=1
132+
)
144133
),
145-
cases(_.color, {c: i for i, c in enumerate("DEFGHIJ", start=1)}),
146-
cases(
147-
_.clarity,
148-
{
149-
c: i
150-
for i, c in enumerate(
151-
("I1", "IF", "SI1", "SI2", "VS1", "VS2", "VVS1", "VVS2"),
152-
start=1,
153-
)
154-
},
134+
_.color.cases((c, i) for i, c in enumerate("DEFGHIJ", start=1)),
135+
_.clarity.cases(
136+
(c, i)
137+
for i, c in enumerate(
138+
("I1", "IF", "SI1", "SI2", "VS1", "VS2", "VVS1", "VVS2"),
139+
start=1,
140+
)
155141
),
156142
)
157143
)

ibis/backends/tests/sql/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,13 @@ def difference(con):
159159
@pytest.fixture(scope="module")
160160
def simple_case(con):
161161
t = con.table("alltypes")
162-
return t.g.case().when("foo", "bar").when("baz", "qux").else_("default").end()
162+
return t.g.cases(("foo", "bar"), ("baz", "qux"), else_="default")
163163

164164

165165
@pytest.fixture(scope="module")
166166
def search_case(con):
167167
t = con.table("alltypes")
168-
return ibis.case().when(t.f > 0, t.d * 2).when(t.c < 0, t.a * 2).end()
168+
return ibis.cases((t.f > 0, t.d * 2), (t.c < 0, t.a * 2))
169169

170170

171171
@pytest.fixture(scope="module")

ibis/backends/tests/sql/snapshots/test_select_sql/test_case_in_projection/decompiled.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,14 @@
2222
lit2 = ibis.literal("bar")
2323

2424
result = alltypes.select(
25-
alltypes.g.case()
26-
.when(lit, lit2)
27-
.when(lit1, ibis.literal("qux"))
28-
.else_(ibis.literal("default"))
29-
.end()
30-
.name("col1"),
31-
ibis.case()
32-
.when((alltypes.g == lit), lit2)
33-
.when((alltypes.g == lit1), alltypes.g)
34-
.else_(ibis.literal(None))
35-
.end()
36-
.name("col2"),
25+
alltypes.g.cases(
26+
(lit, lit2), (lit1, ibis.literal("qux")), else_=ibis.literal("default")
27+
).name("col1"),
28+
ibis.cases(
29+
((alltypes.g == lit), lit2),
30+
((alltypes.g == lit1), alltypes.g),
31+
else_=ibis.literal(None),
32+
).name("col2"),
3733
alltypes.a,
3834
alltypes.b,
3935
alltypes.c,

ibis/backends/tests/sql/test_select_sql.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,8 @@ def test_bool_bool(snapshot):
461461

462462
def test_case_in_projection(alltypes, snapshot):
463463
t = alltypes
464-
expr = t.g.case().when("foo", "bar").when("baz", "qux").else_("default").end()
465-
expr2 = ibis.case().when(t.g == "foo", "bar").when(t.g == "baz", t.g).end()
464+
expr = t.g.cases(("foo", "bar"), ("baz", "qux"), else_=("default"))
465+
expr2 = ibis.cases((t.g == "foo", "bar"), (t.g == "baz", t.g))
466466
expr = t.select(expr.name("col1"), expr2.name("col2"), t)
467467

468468
snapshot.assert_match(to_sql(expr), "out.sql")

ibis/backends/tests/test_aggregation.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ def test_first_last(alltypes, method, filtered, include_null):
611611
# To sanely test this we create a column that is a mix of nulls and a
612612
# single value (or a single value after filtering is applied).
613613
if filtered:
614-
new = alltypes.int_col.cases([(3, 30), (4, 40)])
614+
new = alltypes.int_col.cases((3, 30), (4, 40))
615615
where = _.int_col == 3
616616
else:
617617
new = (alltypes.int_col == 3).ifelse(30, None)
@@ -738,7 +738,7 @@ def test_arbitrary(alltypes, filtered):
738738
# _something_ we create a column that is a mix of nulls and a single value
739739
# (or a single value after filtering is applied).
740740
if filtered:
741-
new = alltypes.int_col.cases([(3, 30), (4, 40)])
741+
new = alltypes.int_col.cases((3, 30), (4, 40))
742742
where = _.int_col == 3
743743
else:
744744
new = (alltypes.int_col == 3).ifelse(30, None)
@@ -1571,9 +1571,7 @@ def collect_udf(v):
15711571

15721572
def test_binds_are_cast(alltypes):
15731573
expr = alltypes.aggregate(
1574-
high_line_count=(
1575-
alltypes.string_col.case().when("1-URGENT", 1).else_(0).end().sum()
1576-
)
1574+
high_line_count=alltypes.string_col.cases(("1-URGENT", 1), else_=0).sum()
15771575
)
15781576

15791577
expr.execute()
@@ -1616,7 +1614,7 @@ def test_agg_name_in_output_column(alltypes):
16161614
def test_grouped_case(backend, con):
16171615
table = ibis.memtable({"key": [1, 1, 2, 2], "value": [10, 30, 20, 40]})
16181616

1619-
case_expr = ibis.case().when(table.value < 25, table.value).else_(ibis.null()).end()
1617+
case_expr = ibis.cases((table.value < 25, table.value), else_=ibis.null())
16201618

16211619
expr = (
16221620
table.group_by(k="key")

ibis/backends/tests/test_conditionals.py

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from collections import Counter
44

55
import pytest
6+
from pytest import param
67

78
import ibis
89

@@ -62,18 +63,13 @@ def test_substitute(backend):
6263
@pytest.mark.parametrize(
6364
"inp, exp",
6465
[
65-
pytest.param(
66-
lambda: ibis.literal(1)
67-
.case()
68-
.when(1, "one")
69-
.when(2, "two")
70-
.else_("other")
71-
.end(),
66+
param(
67+
lambda: ibis.literal(1).cases((1, "one"), (2, "two"), else_="other"),
7268
"one",
7369
id="one_kwarg",
7470
),
75-
pytest.param(
76-
lambda: ibis.literal(5).case().when(1, "one").when(2, "two").end(),
71+
param(
72+
lambda: ibis.literal(5).cases((1, "one"), (2, "two")),
7773
None,
7874
id="fallthrough",
7975
),
@@ -94,13 +90,8 @@ def test_value_cases_column(batting):
9490
np = pytest.importorskip("numpy")
9591

9692
df = batting.to_pandas()
97-
expr = (
98-
batting.RBI.case()
99-
.when(5, "five")
100-
.when(4, "four")
101-
.when(3, "three")
102-
.else_("could be good?")
103-
.end()
93+
expr = batting.RBI.cases(
94+
(5, "five"), (4, "four"), (3, "three"), else_="could be good?"
10495
)
10596
result = expr.execute()
10697
expected = np.select(
@@ -113,7 +104,7 @@ def test_value_cases_column(batting):
113104

114105

115106
def test_ibis_cases_scalar():
116-
expr = ibis.literal(5).case().when(5, "five").when(4, "four").end()
107+
expr = ibis.literal(5).cases((5, "five"), (4, "four"))
117108
result = expr.execute()
118109
assert result == "five"
119110

@@ -128,12 +119,8 @@ def test_ibis_cases_column(batting):
128119

129120
t = batting
130121
df = batting.to_pandas()
131-
expr = (
132-
ibis.case()
133-
.when(t.RBI < 5, "really bad team")
134-
.when(t.teamID == "PH1", "ph1 team")
135-
.else_(t.teamID)
136-
.end()
122+
expr = ibis.cases(
123+
(t.RBI < 5, "really bad team"), (t.teamID == "PH1", "ph1 team"), else_=t.teamID
137124
)
138125
result = expr.execute()
139126
expected = np.select(
@@ -148,5 +135,45 @@ def test_ibis_cases_column(batting):
148135
@pytest.mark.notimpl("clickhouse", reason="special case this and returns 'oops'")
149136
def test_value_cases_null(con):
150137
"""CASE x WHEN NULL never gets hit"""
151-
e = ibis.literal(5).nullif(5).case().when(None, "oops").else_("expected").end()
138+
e = ibis.literal(5).nullif(5).cases((None, "oops"), else_="expected")
152139
assert con.execute(e) == "expected"
140+
141+
142+
@pytest.mark.parametrize(
143+
("example", "expected"),
144+
[
145+
param(lambda: ibis.case().when(True, "yes").end(), "yes", id="top-level-true"),
146+
param(lambda: ibis.case().when(False, "yes").end(), None, id="top-level-false"),
147+
param(
148+
lambda: ibis.case().when(False, "yes").else_("no").end(),
149+
"no",
150+
id="top-level-false-value",
151+
),
152+
param(
153+
lambda: ibis.literal("a").case().when("a", "yes").end(),
154+
"yes",
155+
id="method-true",
156+
),
157+
param(
158+
lambda: ibis.literal("a").case().when("b", "yes").end(),
159+
None,
160+
id="method-false",
161+
),
162+
param(
163+
lambda: ibis.literal("a").case().when("b", "yes").else_("no").end(),
164+
"no",
165+
id="method-false-value",
166+
),
167+
],
168+
)
169+
def test_ibis_case_still_works(con, example, expected):
170+
# test that the soft-deprecated .case() method still works
171+
# https://github.com/ibis-project/ibis/pull/9096
172+
pd = pytest.importorskip("pandas")
173+
174+
with pytest.warns(FutureWarning):
175+
expr = example()
176+
177+
result = con.execute(expr)
178+
179+
assert (expected is None and pd.isna(result)) or result == expected

0 commit comments

Comments
 (0)