Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: when-then-otherwise examples #3492

Merged
merged 18 commits into from
Jul 29, 2024
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 128 additions & 6 deletions altair/vegalite/v5/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,12 +797,15 @@ class When(_BaseWhen):

References
----------
`polars.expr.whenthen <https://github.com/pola-rs/polars/blob/b85c5e0502ca99c77742ee25ba177e6cd11cf100/py-polars/polars/expr/whenthen.py>`__
`polars.when <https://docs.pola.rs/py-polars/html/reference/expressions/api/polars.when.html>`__
"""

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
Expand All @@ -828,6 +831,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):
Expand All @@ -849,7 +868,7 @@ class Then(core.SchemaBase, t.Generic[_C]):

References
----------
`polars.expr.whenthen <https://github.com/pola-rs/polars/blob/b85c5e0502ca99c77742ee25ba177e6cd11cf100/py-polars/polars/expr/whenthen.py>`__
`polars.when <https://docs.pola.rs/py-polars/html/reference/expressions/api/polars.when.html>`__
"""

_schema = {"type": "object"}
Expand Down Expand Up @@ -888,6 +907,24 @@ def otherwise(
``str`` will be encoded as `shorthand<https://altair-viz.github.io/user_guide/encodings/index.html#encoding-shorthands>`__.
**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)
Expand All @@ -896,7 +933,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()
Expand Down Expand Up @@ -937,12 +974,16 @@ 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
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 <https://vega.github.io/vega-lite/docs/predicate.html#equal-predicate>`__'s.
Shortcut for ``alt.datum.field_name == value``, see examples for usage.
Expand All @@ -951,6 +992,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()
Expand Down Expand Up @@ -988,7 +1048,7 @@ class ChainedWhen(_BaseWhen):

References
----------
`polars.expr.whenthen <https://github.com/pola-rs/polars/blob/b85c5e0502ca99c77742ee25ba177e6cd11cf100/py-polars/polars/expr/whenthen.py>`__
`polars.when <https://docs.pola.rs/py-polars/html/reference/expressions/api/polars.when.html>`__
"""

def __init__(
Expand All @@ -1000,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.
Expand All @@ -1017,6 +1084,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()
Expand Down Expand Up @@ -1047,6 +1134,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 <https://vega.github.io/vega-lite/docs/predicate.html#equal-predicate>`__'s.
Shortcut for ``alt.datum.field_name == value``, see examples for usage.
Expand All @@ -1066,9 +1157,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")
Expand Down