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

Conversation

dangotbanned
Copy link
Member

@dangotbanned dangotbanned commented Jul 21, 2024

Follow-up referenced in #3427 (comment)

Developing each function/method doc further with example(s)

Tasks

  • empty note (adapted from docs: Add empty as a explicit condition kwarg #3490)
  • Each method has a situational example
    • Then.when
    • Then.otherwise
    • When.then
    • ChainedWhen.then
  • Multiple examples
    • alt.when (using vega_datasets)
  • Adding a __repr__ for When, ChainedWhen
    • Then inherits one

Possible tasks

Just some ideas that came up while working on this.
Can work on them if there's interest, but not planning to otherwise:

  • Moving parts of the Parameters section into examples
    • E.g. empty note

Comments from original PR

Linking these as I raised them as potential issues, but they were not discussed:

@dangotbanned dangotbanned changed the title docs: when-then-otherwise examples docs(DRAFT): when-then-otherwise examples Jul 21, 2024
@dangotbanned dangotbanned changed the title docs(DRAFT): when-then-otherwise examples docs: when-then-otherwise examples Jul 24, 2024
@dangotbanned dangotbanned marked this pull request as ready for review July 24, 2024 12:42
@mattijn
Copy link
Contributor

mattijn commented Jul 25, 2024

Thanks! This looks great. I was expecting these examples to be around here: https://altair-viz.github.io/user_guide/interactions.html#conditions-filters, but the examples in this PR are part of the docstrings, which is very nice too! We should have more of these.

Will add a note to #3500.

I noticed when playing with this that I agree with your suggestion that it would be nice to have a __repr__ for When and ChainedWhen.

Regarding, your three linked comments from original PR. I don't have clear insight on these either. I don't think it is necessary to use intersphinx, but a reference to the polars docs page of the pl.when syntax makes sense though.

@dangotbanned
Copy link
Member Author

Thanks! This looks great. I was expecting these examples to be around here: altair-viz.github.io/user_guide/interactions.html#conditions-filters, but the examples in this PR are part of the docstrings, which is very nice too! We should have more of these.

Will add a note to #3500.

Thank you for reviewing, but my bad for misunderstanding the assignment @mattijn

Hopefully they will make writing the User Guide parts easier in #3500, since most of the examples already produce a valid chart.
The last one for alt.when I wanted to adapt - but the cars dataset wasn't a good fit.


I noticed when playing with this that I agree with your suggestion that it would be nice to have a __repr__ for When and ChainedWhen.

For reference, following 0e7b49f (#3492)

image

The ChainedWhen one is tricky as it represents

  • a list of complete conditions (self._conditions)
  • a single partial condition (self._condition), prior to the next .then()

@dangotbanned
Copy link
Member Author

@mattijn are we good to go with this one?

Sorry I can't seem to request a review

dangotbanned added a commit to dangotbanned/altair that referenced this pull request Jul 29, 2024
altair/vegalite/v5/api.py Outdated Show resolved Hide resolved
Co-authored-by: Mattijn van Hoek <mattijn@gmail.com>
Copy link
Contributor

@mattijn mattijn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. One suggested change.

I like the examples. Really clean.

# 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")),
)
# 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)
 # 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)
# 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
)
# 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")
    & (alt.datum.Color == "Green")
    & (alt.datum.Age == 25)
    & (alt.datum.StartDate == "2000-10-01")
)
when_verbose = alt.when(verbose_composition)
# To
when_concise = alt.when(Name="Name_1", Color="Green", Age=25, StartDate="2000-10-01")

@mattijn mattijn merged commit e4fb2c9 into vega:main Jul 29, 2024
13 checks passed
@dangotbanned
Copy link
Member Author

Thanks @mattijn for the review and well spotted on the missing )

Seeing them all together in #3492 (review) really looks like this will fit into the User Guide nicely

@mattijn
Copy link
Contributor

mattijn commented Jul 29, 2024

Yes! Beautiful new addition to the Altair library! Well done @dangotbanned!

@dangotbanned dangotbanned deleted the when-then-docs-detail branch July 30, 2024 07:38
binste pushed a commit that referenced this pull request Aug 2, 2024
* ci(ruff): Add `ANN` rules for `api.py` only

To highlight all the missing annotations to fix and autofix `None` return

* feat(typing): Complete annotations for most `api` functions

Excluding `*args` on `ChartType` wrappers. They need to be defined in alignment in multiple places, which is more complex

* feat(typing): Annotate more expr/params in `api`

* feat(typing): Various changes to enforce `dict[str, Any]` instead of `dict`

Among these, many locations already assume `str` keys in the implementation - without checking

* feat(typing): Misc minor method annotations

* feat(typing): Use `ChartType` in all `ChartType` dunder methods

* fix(ruff): Ignore some `ANN` rules that won't be fixed

* chore: add pyright ignore from #3492

* feat(typing): Improve `ChartType` constructor/factory annotations

* feat(typing): Annotate remaining functions in `api`

* feat(typing): Complete `RepeatChart` annotations

* fix(typing): Resolve Liskov violations

```
altair\vegalite\v5\api.py:4368: error: Argument 1 of "__iadd__" is incompatible with "__add__" of supertype "TopLevelMixin"; supertype defines the argument type as "Chart | RepeatChart | ConcatChart | HConcatChart | VConcatChart | FacetChart | LayerChart"  [override]         def __iadd__(self, other: LayerChart | Chart) -> Self:                            ^~~~~~~~~~~~~~~~~~~~~~~~~ altair\vegalite\v5\api.py:4368: note: This violates the Liskov substitution principle altair\vegalite\v5\api.py:4368: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides altair\vegalite\v5\api.py:4376: error: Argument 1 of "__add__" is incompatible with supertype "TopLevelMixin"; supertype defines the argument type as "Chart | RepeatChart | ConcatChart | HConcatChart | VConcatChart | FacetChart | LayerChart"  [override]         def __add__(self, other: LayerChart | Chart) -> Self:                           ^~~~~~~~~~~~~~~~~~~~~~~~~ altair\vegalite\v5\api.py:4376: note: This violates the Liskov substitution principle altair\vegalite\v5\api.py:4376: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
```

* chore(typing): Update more `dict` -> `dict[str, Any]`

* style(ruff): fix whitespace

* chore: Remove TODO fixed in #3480

* fix(typing): Enable `ANN003` and fix all in `api`

* fix(typing): Enable `ANN002` and fix all in `api`

* chore(ruff): Add note on `ANN`

This could later be extended to other modules, but for now `api` is complete.

* fix(typing): Add missing `FacetChart` annotations

To align with the other `ChartType`s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants