|
127 | 127 | NamedData, |
128 | 128 | ParameterName, |
129 | 129 | PointSelectionConfig, |
130 | | - Predicate, |
131 | 130 | PredicateComposition, |
132 | 131 | ProjectionType, |
133 | 132 | RepeatMapping, |
@@ -542,12 +541,19 @@ def check_fields_and_encodings(parameter: Parameter, field_name: str) -> bool: |
542 | 541 | """ |
543 | 542 |
|
544 | 543 |
|
545 | | -_FieldEqualType: TypeAlias = Union[PrimitiveValue_T, Map, Parameter, SchemaBase] |
546 | | -"""Permitted types for equality checks on field values: |
| 544 | +_FieldEqualType: TypeAlias = Union["IntoExpression", Parameter, SchemaBase] |
| 545 | +""" |
| 546 | +Permitted types for equality checks on field values. |
| 547 | +
|
| 548 | +Applies to the following context(s): |
| 549 | +
|
| 550 | + import altair as alt |
547 | 551 |
|
548 | | -- `datum.field == ...` |
549 | | -- `FieldEqualPredicate(equal=...)` |
550 | | -- `when(**constraints=...)` |
| 552 | + alt.datum.field == ... |
| 553 | + alt.FieldEqualPredicate(field="field", equal=...) |
| 554 | + alt.when(field=...) |
| 555 | + alt.when().then().when(field=...) |
| 556 | + alt.Chart.transform_filter(field=...) |
551 | 557 | """ |
552 | 558 |
|
553 | 559 |
|
@@ -2986,45 +2992,113 @@ def transform_extent( |
2986 | 2992 | """ |
2987 | 2993 | return self._add_transform(core.ExtentTransform(extent=extent, param=param)) |
2988 | 2994 |
|
2989 | | - # TODO: Update docstring |
2990 | | - # # E.g. {'not': alt.FieldRangePredicate(field='year', range=[1950, 1960])} |
2991 | 2995 | def transform_filter( |
2992 | 2996 | self, |
2993 | | - filter: str |
2994 | | - | Expr |
2995 | | - | Expression |
2996 | | - | Predicate |
2997 | | - | Parameter |
2998 | | - | PredicateComposition |
2999 | | - | dict[str, Predicate | str | list | bool], |
3000 | | - **kwargs: Any, |
| 2997 | + predicate: Optional[_PredicateType] = Undefined, |
| 2998 | + *more_predicates: _ComposablePredicateType, |
| 2999 | + empty: Optional[bool] = Undefined, |
| 3000 | + **constraints: _FieldEqualType, |
3001 | 3001 | ) -> Self: |
3002 | 3002 | """ |
3003 | | - Add a :class:`FilterTransform` to the schema. |
| 3003 | + Add a :class:`FilterTransform` to the spec. |
| 3004 | +
|
| 3005 | + The resulting predicate is an ``&`` reduction over ``predicate`` and optional ``*``, ``**``, arguments. |
3004 | 3006 |
|
3005 | 3007 | Parameters |
3006 | 3008 | ---------- |
3007 | | - filter : a filter expression or :class:`PredicateComposition` |
3008 | | - The `filter` property must be one of the predicate definitions: |
3009 | | - (1) a string or alt.expr expression |
3010 | | - (2) a range predicate |
3011 | | - (3) a selection predicate |
3012 | | - (4) a logical operand combining (1)-(3) |
3013 | | - (5) a Selection object |
| 3009 | + predicate |
| 3010 | + A selection or test predicate. ``str`` input will be treated as a test operand. |
| 3011 | + *more_predicates |
| 3012 | + Additional predicates, restricted to types supporting ``&``. |
| 3013 | + empty |
| 3014 | + For selection parameters, the predicate of empty selections returns ``True`` by default. |
| 3015 | + Override this behavior, with ``empty=False``. |
3014 | 3016 |
|
3015 | | - Returns |
3016 | | - ------- |
3017 | | - self : Chart object |
3018 | | - returns chart to allow for chaining |
| 3017 | + .. note:: |
| 3018 | + When ``predicate`` is a ``Parameter`` that is used more than once, |
| 3019 | + ``self.transform_filter(..., empty=...)`` provides granular control for each occurrence. |
| 3020 | + **constraints |
| 3021 | + Specify `Field Equal Predicate`_'s. |
| 3022 | + Shortcut for ``alt.datum.field_name == value``, see examples for usage. |
| 3023 | +
|
| 3024 | + Warns |
| 3025 | + ----- |
| 3026 | + AltairDeprecationWarning |
| 3027 | + If called using ``filter`` as a keyword argument. |
| 3028 | +
|
| 3029 | + See Also |
| 3030 | + -------- |
| 3031 | + alt.when : Uses a similar syntax for defining conditional values. |
| 3032 | +
|
| 3033 | + Notes |
| 3034 | + ----- |
| 3035 | + - Directly inspired by the syntax used in `polars.DataFrame.filter`_. |
| 3036 | +
|
| 3037 | + .. _Field Equal Predicate: |
| 3038 | + https://vega.github.io/vega-lite/docs/predicate.html#equal-predicate |
| 3039 | + .. _polars.DataFrame.filter: |
| 3040 | + https://docs.pola.rs/api/python/stable/reference/dataframe/api/polars.DataFrame.filter.html |
| 3041 | +
|
| 3042 | + Examples |
| 3043 | + -------- |
| 3044 | + Setting up a common chart:: |
| 3045 | +
|
| 3046 | + import altair as alt |
| 3047 | + from altair import datum |
| 3048 | + from vega_datasets import data |
| 3049 | +
|
| 3050 | + source = data.population.url |
| 3051 | + chart = ( |
| 3052 | + alt.Chart(source) |
| 3053 | + .mark_line() |
| 3054 | + .encode( |
| 3055 | + x="age:O", |
| 3056 | + y="sum(people):Q", |
| 3057 | + color=alt.Color("year:O").legend(symbolType="square"), |
| 3058 | + ) |
| 3059 | + ) |
| 3060 | + chart |
| 3061 | +
|
| 3062 | + Singular predicates can be expressed via ``datum``:: |
| 3063 | +
|
| 3064 | + chart.transform_filter(datum.year <= 1980) |
| 3065 | +
|
| 3066 | + We can also use selection parameters directly:: |
| 3067 | +
|
| 3068 | + selection = alt.selection_point(encodings=["color"], bind="legend") |
| 3069 | + chart.transform_filter(selection).add_params(selection) |
| 3070 | +
|
| 3071 | + Or a field predicate:: |
| 3072 | +
|
| 3073 | + between_1950_60 = alt.FieldRangePredicate(field="year", range=[1950, 1960]) |
| 3074 | + chart.transform_filter(between_1950_60) | chart.transform_filter(~between_1950_60) |
| 3075 | +
|
| 3076 | + Predicates can be composed together using logical operands:: |
| 3077 | +
|
| 3078 | + chart.transform_filter(between_1950_60 | (datum.year == 1850)) |
| 3079 | +
|
| 3080 | + Predicates passed as positional arguments will be reduced with ``&``:: |
| 3081 | +
|
| 3082 | + chart.transform_filter(datum.year > 1980, datum.age != 90) |
| 3083 | +
|
| 3084 | + Using keyword-argument ``constraints`` can simplify compositions like:: |
| 3085 | +
|
| 3086 | + verbose_composition = chart.transform_filter((datum.year == 2000) & (datum.sex == 1)) |
| 3087 | + chart.transform_filter(year=2000, sex=1) |
3019 | 3088 | """ |
3020 | | - if isinstance(filter, Parameter): |
3021 | | - new_filter: dict[str, Any] = {"param": filter.name} |
3022 | | - if "empty" in kwargs: |
3023 | | - new_filter["empty"] = kwargs.pop("empty") |
3024 | | - elif isinstance(filter.empty, bool): |
3025 | | - new_filter["empty"] = filter.empty |
3026 | | - filter = new_filter |
3027 | | - return self._add_transform(core.FilterTransform(filter=filter, **kwargs)) |
| 3089 | + if depr_filter := t.cast(Any, constraints.pop("filter", None)): |
| 3090 | + utils.deprecated_warn( |
| 3091 | + "Passing `filter` as a keyword is ambiguous.\n\n" |
| 3092 | + "Use a positional argument for `<5.5.0` behavior.\n" |
| 3093 | + "Or, `alt.datum['filter'] == ...` if referring to a column named 'filter'.", |
| 3094 | + version="5.5.0", |
| 3095 | + ) |
| 3096 | + if utils.is_undefined(predicate): |
| 3097 | + predicate = depr_filter |
| 3098 | + else: |
| 3099 | + more_predicates = *more_predicates, depr_filter |
| 3100 | + cond = _parse_when(predicate, *more_predicates, empty=empty, **constraints) |
| 3101 | + return self._add_transform(core.FilterTransform(filter=cond.get("test", cond))) |
3028 | 3102 |
|
3029 | 3103 | def transform_flatten( |
3030 | 3104 | self, |
|
0 commit comments