.. currentmodule:: altair .. _binding-parameters: Bindings & Widgets ~~~~~~~~~~~~~~~~~~ With an understanding of the parameter types and conditions, you can now bind parameters to chart elements (e.g. legends) and widgets (e.g. drop-downs and sliders). This is done using the ``bind`` option inside ``param`` and ``selection``. As specified by `the Vega-lite binding docs `_, there are three types of bindings available: 1. Point and interval selections can be used for data-driven interactive elements, such as highlighting and filtering based on values in the data. 2. Sliders and checkboxes can be used for logic-driven interactive elements, such as highlighting and filtering based on the absolute values in these widgets. 3. Interval selections can be bound to a scale, such as zooming in on a map. The following table summarizes the input elements that are supported in Vega-Lite: ========================= =========================================================================== =============================================== Input Element Description Example ========================= =========================================================================== =============================================== :class:`binding_checkbox` Renders as checkboxes allowing for multiple selections of items. :ref:`gallery_multiple_interactions` :class:`binding_radio` Radio buttons that force only a single selection :ref:`gallery_multiple_interactions` :class:`binding_select` Drop down box for selecting a single item from a list :ref:`gallery_multiple_interactions` :class:`binding_range` Shown as a slider to allow for selection along a scale. :ref:`gallery_us_population_over_time` :class:`binding` General method that supports many HTML input elements ========================= =========================================================================== =============================================== Widget Binding ^^^^^^^^^^^^^^ Widgets are HTML input elements, such as drop-downs, sliders, radio buttons, and search boxes. There are a three strategies for how variable and selection parameters can be used together with widgets: data-driven lookups, data-driven comparisons, and logic-driven comparisons. Data-Driven Lookups ------------------- Data-driven lookups use the active value(s) of the widget together with a ``selection`` parameter to look up points with matching values in the chart's dataset. For example, we can establish a binding between an input widget and a point selection to filter the data as in the example below where a drop-down is used to highlight cars of a specific ``Origin``: .. altair-plot:: import altair as alt from altair.datasets import data cars = data.cars.url input_dropdown = alt.binding_select(options=['Europe', 'Japan', 'USA'], name='Region ') selection = alt.selection_point(fields=['Origin'], bind=input_dropdown) color = ( alt.when(selection) .then(alt.Color("Origin:N").legend(None)) .otherwise(alt.value("lightgray")) ) alt.Chart(cars).mark_point().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', color=color, ).add_params( selection ) Note that although it looks like a value is selected in the dropdown from the start, we need to set `value=` to actually start out with an initial selection in the chart. We did this previously with variable parameters and selection parameters follow the same pattern as you will see further down in the :ref:`encoding-channel-binding` section. As you can see above, we are still using :ref:`conditions ` to make the chart respond to the selection, just as we did without widgets. Bindings and input elements can also be used to filter data allowing the user to see just the selected points as in the example below. In this example, we also add an empty selection to illustrate how to revert to showing all points after a selection has been made in a radio button or drop-down (which cannot be deselected). .. altair-plot:: # Make radio button less cramped by adding a space after each label # The spacing will only show up in your IDE, not on this doc page options = ['Europe', 'Japan', 'USA'] labels = [option + ' ' for option in options] input_dropdown = alt.binding_radio( # Add the empty selection which shows all when clicked options=options + [None], labels=labels + ['All'], name='Region: ' ) selection = alt.selection_point( fields=['Origin'], bind=input_dropdown, ) alt.Chart(cars).mark_point().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', # We need to set a constant domain to preserve the colors # when only one region is shown at a time color=alt.Color('Origin:N').scale(domain=options), ).add_params( selection ).transform_filter( selection ) In addition to the widgets listed in the table above, Altair has access to `any html widget `_ via the more general ``binding`` function. In the example below, we use a search input to filter points that match the search string exactly. You can hover over the points to see the car names and try typing one into the search box, e.g. ``vw pickup`` to see the point highlighted (you need to type out the full name). .. altair-plot:: search_input = alt.selection_point( fields=['Name'], empty=False, # Start with no points selected bind=alt.binding( input='search', placeholder="Car model", name='Search ', ) ) alt.Chart(data.cars.url).mark_point(size=60).encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', tooltip='Name:N', opacity=alt.when(search_input).then(alt.value(1)).otherwise(alt.value(0.05)), ).add_params( search_input ) It is not always useful to require an exact match to the search syntax, and when we will be learning about :ref:`expressions`, we will see how we can match partial strings via a regex instead. Data-Driven Comparisons ----------------------- So far we have seen the use of selections to lookup points with precisely matching values in our data. This is often useful, but sometimes we might want to make a more complex comparison than an exact match. For example, we might want to create a condition we select the points in the data that are above or below a threshold value, which is specified via a slider. For this workflow it is recommended to use variable parameters via ``param`` and as you can see below, we use the special syntax ``datum.xval`` to reference the column to compare against. Prefixing the column name with ``datum`` tells Altair that we want to compare to a column in the dataframe, rather than to a Python variable called ``xval``, which would have been the case if we just wrote ``xval < selector``. .. altair-plot:: import numpy as np import pandas as pd rand = np.random.RandomState(42) df = pd.DataFrame({ 'xval': range(100), 'yval': rand.randn(100).cumsum() }) slider = alt.binding_range(min=0, max=100, step=1, name='Cutoff ') selector = alt.param(name='SelectorName', value=50, bind=slider) predicate = alt.datum.xval < selector alt.Chart(df).mark_point().encode( x='xval', y='yval', color=alt.when(predicate).then(alt.value("red")).otherwise(alt.value("blue")), ).add_params( selector ) In this particular case we could actually have used a selection parameter since selection values can be accessed directly and used in expressions that affect the chart. For example, here we create a slider to choose a cutoff value, and color points based on whether they are smaller or larger than the value: .. altair-plot:: slider = alt.binding_range(min=0, max=100, step=1, name='Cutoff ') selector = alt.selection_point( name="SelectorName", fields=['cutoff'], bind=slider, value=[{'cutoff': 50}] ) predicate = alt.datum.xval < selector.cutoff alt.Chart(df).mark_point().encode( x='xval', y='yval', color=alt.when(predicate).then(alt.value("red")).otherwise(alt.value("blue")), ).add_params( selector ) While it can be useful to know how to access selection values in expression strings, using the parameters syntax introduced in Altair 5 often provides a more convenient syntax for simple interactions like this one since they can also be accessed in expression strings as we saw above. Similarly, it is often possible to use equality statements such as ``alt.datum.xval == selector`` to lookup exact values but it is often more convenient to switch to a selection parameter and specify a field/encoding. Logic-Driven Comparisons ------------------------ A logic comparison is a type of comparison that is based on logical rules and conditions, rather than on the actual data values themselves. For example, for a checkbox widget we want to check if the state of the checkbox is True or False and execute some action depending on whether it is checked or not. When we are using a checkbox as a toggle like this, we need to use `param` instead of `selection_point`, since we don't want to check if there are True/False values in our data, just if the value of the check box is True (checked) or False (unchecked): .. altair-plot:: bind_checkbox = alt.binding_checkbox(name='Scale point size by "Acceleration": ') param_checkbox = alt.param(bind=bind_checkbox) alt.Chart(cars).mark_point().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', size=alt.when(param_checkbox).then("Acceleration:Q").otherwise(alt.value(25)), ).add_params( param_checkbox ) Another example of creating a widget binding that is independent of the data, involves an interesting use case for the more general ``binding`` function. In the next example, this function introduces a color picker where the user can choose the colors of the chart interactively: .. altair-plot:: color_usa = alt.param(value="#317bb4", bind=alt.binding(input='color', name='USA ')) color_europe = alt.param(value="#ffb54d", bind=alt.binding(input='color', name='Europe ')) color_japan = alt.param(value="#adadad", bind=alt.binding(input='color', name='Japan ')) alt.Chart(data.cars.url).mark_circle().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', color=alt.Color( 'Origin:N', scale=alt.Scale( domain=['USA', 'Europe', 'Japan'], range=[color_usa, color_europe, color_japan] ) ) ).add_params( color_usa, color_europe, color_japan ) .. _legend-binding: Legend Binding ^^^^^^^^^^^^^^ An interactive legend can often be helpful to assist in focusing in on groups of data. Instead of manually having to build a separate chart to use as a legend, Altair provides the ``bind='legend'`` option to facilitate the creation of clickable legends: .. altair-plot:: selection = alt.selection_point(fields=['Origin'], bind='legend') alt.Chart(cars).mark_point().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', color='Origin:N', opacity=alt.when(selection).then(alt.value(0.8)).otherwise(alt.value(0.2)), ).add_params( selection ) Scale Binding ^^^^^^^^^^^^^ With interval selections, the ``bind`` property can be set to the value of ``"scales"``. In these cases, the binding will automatically respond to the panning and zooming along the chart: .. altair-plot:: selection = alt.selection_interval(bind='scales') alt.Chart(cars).mark_point().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', color='Origin:N', ).add_params( selection ) Because this is such a common pattern, Altair provides the :meth:`interactive` method which creates a scale-bound selection more concisely: .. altair-plot:: alt.Chart(cars).mark_point().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', color='Origin:N', ).interactive() .. _encoding-channel-binding: Encoding Channel Binding ^^^^^^^^^^^^^^^^^^^^^^^^ To update which columns are displayed in a chart based on the selection in a widget, we would need to bind the widget to an encoding channel. In contrast to legend and scale bindings, it is not possible to setup a binding to an encoding channel in the selection initialization (e.g. by typing ``bind='x'``). Instead, parameters can be used to pass the value of a selection to an encoding channel. This gives more flexibility, but requires the use of a separate calculation transform (as in the example below) until https://github.com/vega/vega-lite/issues/7365 is resolved. In this example, we access the parameter value by referencing the parameter by name. By indexing the data with the parameter value (via ``datum[]``) we can extract the data column that matches the selected value of the parameter, and populate the x-channel with the values from this data column. .. altair-plot:: dropdown = alt.binding_select( options=['Horsepower', 'Displacement', 'Weight_in_lbs', 'Acceleration'], name='X-axis column ' ) xcol_param = alt.param( value='Horsepower', bind=dropdown ) alt.Chart(data.cars.url).mark_circle().encode( x=alt.X('x:Q').title(''), y='Miles_per_Gallon:Q', color='Origin:N' ).transform_calculate( x=alt.datum[xcol_param] # x=f'datum[{xcol_param.name}]' # Also works ).add_params( xcol_param ) Using parameters inside calculate transforms allows us to define dynamic computations (e.g. subtracting different pairs of columns), as you can see in the :ref:`gallery_interactive_column_selection` gallery example. In that example, the chart title is also dynamically updated using a parameter inside an expression which is described in more detail in :ref:`accessing-parameter-values`. Note that it is currently not possible to change the axis titles dynamically based on the selected parameter value, but a text mark could be used instead (as in `this SO answer `_), until https://github.com/vega/vega-lite/issues/7264 is resolved.