From 94764a22f643a68da6f9375e6552a5b6664e4f0d Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Sun, 21 Jul 2024 19:23:24 +0100 Subject: [PATCH 01/10] docs: adapt `empty` note for `when`, `Then.when` --- altair/vegalite/v5/api.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index 19ff95100..4428e7a7c 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -913,6 +913,10 @@ def when( empty For selection parameters, the predicate of empty selections returns ``True`` by default. Override this behavior, with ``empty=False``. + + .. note:: + When ``predicate`` is a ``Parameter`` that is used more than once, + ``alt.when().then().when(..., empty=...)`` provides granular control for each occurrence. **constraints Specify `Field Equal Predicate `__'s. Shortcut for ``alt.datum.field_name == value``, see examples for usage. @@ -1015,6 +1019,10 @@ def when( empty For selection parameters, the predicate of empty selections returns ``True`` by default. Override this behavior, with ``empty=False``. + + .. note:: + When ``predicate`` is a ``Parameter`` that is used more than once, + ``alt.when(..., empty=...)`` provides granular control for each occurrence. **constraints Specify `Field Equal Predicate `__'s. Shortcut for ``alt.datum.field_name == value``, see examples for usage. From cf5e6766c0430a851a48329cac7170534eb2f504 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:48:29 +0100 Subject: [PATCH 02/10] docs: Add example for `Then.when` --- altair/vegalite/v5/api.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index 4428e7a7c..1f4aa7840 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -907,7 +907,7 @@ def when( A selection or test predicate. ``str`` input will be treated as a test operand. .. note:: - accepts the same range of inputs as in :func:`.condition()`. + Accepts the same range of inputs as in :func:`.condition()`. *more_predicates Additional predicates, restricted to types supporting ``&``. empty @@ -925,6 +925,25 @@ def when( ------- :class:`ChainedWhen` A partial state which requires calling :meth:`ChainedWhen.then()` to finish the condition. + + + Examples + -------- + Chain calls to express precise queries:: + + import altair as alt + from vega_datasets import data + + source = data.cars() + color = ( + alt.when(alt.datum.Miles_per_Gallon >= 30, Origin="Europe") + .then(alt.value("crimson")) + .when(alt.datum.Horsepower > 150) + .then(alt.value("goldenrod")) + .otherwise(alt.value("grey")) + ) + + alt.Chart(source).mark_point().encode(x="Horsepower", y="Miles_per_Gallon", color=color) """ condition = _parse_when(predicate, *more_predicates, empty=empty, **constraints) conditions = self.to_dict() From da1401a7dfee439a2f30a7d10651b78776b96a9c Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:19:20 +0100 Subject: [PATCH 03/10] docs: Add an example to `Then.otherwise` --- altair/vegalite/v5/api.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index e913d5a14..050eea783 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -888,6 +888,24 @@ def otherwise( ``str`` will be encoded as `shorthand`__. **kwds Additional keyword args are added to the resulting ``dict``. + + + Examples + -------- + Points outside of ``brush`` will not appear highlighted:: + + import altair as alt + from vega_datasets import data + + source = data.cars() + brush = alt.selection_interval() + color = alt.when(brush).then("Origin:N").otherwise(alt.value("grey")) + + alt.Chart(source).mark_point().encode( + x="Horsepower:Q", + y="Miles_per_Gallon:Q", + color=color, + ).add_params(brush) """ conditions: _Conditional[Any] is_extra = functools.partial(_is_condition_extra, kwds=kwds) From 6dae002100833ec08a04322c16887ee72a5904b1 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:30:32 +0100 Subject: [PATCH 04/10] chore(typing): add pyright ignore Wasn't picked up in #3480 --- altair/vegalite/v5/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index 050eea783..4180e0924 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -914,7 +914,7 @@ def otherwise( if isinstance(current, list) and len(current) == 1: # This case is guaranteed to have come from `When` and not `ChainedWhen` # The `list` isn't needed if we complete the condition here - conditions = _Conditional(condition=current[0]) + conditions = _Conditional(condition=current[0]) # pyright: ignore[reportArgumentType] elif isinstance(current, dict): if not is_extra(statement): conditions = self.to_dict() From abff0f0f6da82c99cb61644164a57c696cdc6fce Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:52:01 +0100 Subject: [PATCH 05/10] docs: Add example for `When.then` Adapted from the final example in https://vega.github.io/vega-lite/docs/condition.html --- altair/vegalite/v5/api.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index 4180e0924..275dac82f 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -828,6 +828,22 @@ def then(self, statement: _StatementType, /, **kwds: Any) -> Then[Any]: Returns ------- :class:`Then` + + Examples + -------- + Simple conditions may be expressed without defining a default:: + + import altair as alt + from vega_datasets import data + + source = data.movies() + predicate = (alt.datum.IMDB_Rating == None) | (alt.datum.Rotten_Tomatoes_Rating == None) + + alt.Chart(source).mark_point(invalid=None).encode( + x="IMDB_Rating:Q", + y="Rotten_Tomatoes_Rating:Q", + color=alt.when(predicate).then(alt.value("grey")), + ) """ condition = self._when_then(statement, kwds) if _is_condition_extra(condition, statement, kwds=kwds): From 7b729e2814d1bf2c3ce118f93fca518903c4419e Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Wed, 24 Jul 2024 11:07:02 +0100 Subject: [PATCH 06/10] docs: Add example for `ChainedWhen.then` --- altair/vegalite/v5/api.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index 275dac82f..bfd535525 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -1074,6 +1074,26 @@ def then(self, statement: _StatementType, /, **kwds: Any) -> Then[_Conditions]: Returns ------- :class:`Then` + + Examples + -------- + Multiple conditions with an implicit default:: + + import altair as alt + from vega_datasets import data + + source = data.movies() + predicate = (alt.datum.IMDB_Rating == None) | (alt.datum.Rotten_Tomatoes_Rating == None) + color = ( + alt.when(predicate) + .then(alt.value("grey")) + .when(alt.datum.IMDB_Votes < 5000) + .then(alt.value("lightblue")) + ) + + alt.Chart(source).mark_point(invalid=None).encode( + x="IMDB_Rating:Q", y="Rotten_Tomatoes_Rating:Q", color=color + ) """ condition = self._when_then(statement, kwds) conditions = self._conditions.copy() From 5478f354ed0744bd0ddca38afe1439fdb495dd52 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:50:08 +0100 Subject: [PATCH 07/10] docs: Add real-world examples for `alt.when` Covers everything apart from chained when --- altair/vegalite/v5/api.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index bfd535525..cb7c3f283 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -1147,9 +1147,40 @@ def when( Examples -------- - Using keyword-argument ``constraints`` can simplify compositions like:: + Setting up a common chart:: import altair as alt + from vega_datasets import data + + source = data.cars() + brush = alt.selection_interval() + points = ( + alt.Chart(source) + .mark_point() + .encode(x="Horsepower", y="Miles_per_Gallon") + .add_params(brush) + ) + points + + Basic ``if-then-else`` conditions translate directly to ``when-then-otherwise``:: + + points.encode(color=alt.when(brush).then("Origin").otherwise(alt.value("lightgray")) + + Omitting the ``.otherwise()`` clause will use the channel default instead:: + + points.encode(color=alt.when(brush).then("Origin")) + + Predicates passed as positional arguments will be reduced with ``&``:: + + points.encode( + color=alt.when( + brush, (alt.datum.Miles_per_Gallon >= 30) | (alt.datum.Horsepower >= 130) + ) + .then("Origin") + .otherwise(alt.value("lightgray")) + ) + + Using keyword-argument ``constraints`` can simplify compositions like:: verbose_composition = ( (alt.datum.Name == "Name_1") From 756d6a1ad648d43ed0a4564f4d5abe0b16da940f Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:37:46 +0100 Subject: [PATCH 08/10] docs: Update `polars.when` references Now all link to docs, rather than source code https://github.com/vega/altair/pull/3492#issuecomment-2251475156 --- altair/vegalite/v5/api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index cb7c3f283..cd184fcf8 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -797,7 +797,7 @@ class When(_BaseWhen): References ---------- - `polars.expr.whenthen `__ + `polars.when `__ """ def __init__(self, condition: _ConditionType, /) -> None: @@ -865,7 +865,7 @@ class Then(core.SchemaBase, t.Generic[_C]): References ---------- - `polars.expr.whenthen `__ + `polars.when `__ """ _schema = {"type": "object"} @@ -1045,7 +1045,7 @@ class ChainedWhen(_BaseWhen): References ---------- - `polars.expr.whenthen `__ + `polars.when `__ """ def __init__( From 0e7b49f45807aac494861465b9a734ea8e74def0 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:50:40 +0100 Subject: [PATCH 09/10] feat: Add `__repr__` for `When`, `ChainedWhen` https://github.com/vega/altair/pull/3492#issuecomment-2251475156 --- altair/vegalite/v5/api.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index cd184fcf8..f51083810 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -803,6 +803,9 @@ class When(_BaseWhen): def __init__(self, condition: _ConditionType, /) -> None: self._condition = condition + def __repr__(self) -> str: + return f"{type(self).__name__}({self._condition!r})" + @overload def then(self, statement: str, /, **kwds: Any) -> Then[_Condition]: ... @overload @@ -1057,6 +1060,13 @@ def __init__( self._condition = condition self._conditions = conditions + def __repr__(self) -> str: + return ( + f"{type(self).__name__}(\n" + f" {self._conditions!r},\n {self._condition!r}\n" + ")" + ) + def then(self, statement: _StatementType, /, **kwds: Any) -> Then[_Conditions]: """ Attach a statement to this predicate. From aa0e145e9f10c6988924fad14c3624e4216124df Mon Sep 17 00:00:00 2001 From: Dan Redding <125183946+dangotbanned@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:43:40 +0100 Subject: [PATCH 10/10] Update altair/vegalite/v5/api.py Co-authored-by: Mattijn van Hoek --- altair/vegalite/v5/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index f51083810..3c21f5764 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -1174,7 +1174,7 @@ def when( Basic ``if-then-else`` conditions translate directly to ``when-then-otherwise``:: - points.encode(color=alt.when(brush).then("Origin").otherwise(alt.value("lightgray")) + points.encode(color=alt.when(brush).then("Origin").otherwise(alt.value("lightgray"))) Omitting the ``.otherwise()`` clause will use the channel default instead::