Привязки и виджеты

Зная типы параметров и условия, можно связывать параметры с элементами диаграммы, например легендами, и виджетами, например списками и ползунками. Для этого используется bind внутри param и selection. Согласно документации привязок Vega-Lite, доступны три подхода:

  1. Точечный и интервальный выбор для интерактивности на основе данных: выделения и фильтрации по значениям.
  2. Ползунки и флажки для интерактивности на основе логики: выделения и фильтрации по значениям самих виджетов.
  3. Привязка интервального выбора к шкале, например для масштабирования карты.

Vega-Lite поддерживает следующие элементы ввода:

Элемент Описание Пример
binding_checkbox Флажки, позволяющие выбирать несколько элементов. Несколько видов взаимодействия
binding_radio Переключатели с выбором только одного варианта. Несколько видов взаимодействия
binding_select Раскрывающийся список для выбора одного элемента. Несколько видов взаимодействия
binding_range Ползунок для выбора значения по шкале. Население США во времени
binding Общий способ, поддерживающий многие элементы ввода HTML.

Привязка виджетов

Виджеты — элементы HTML: списки, ползунки, переключатели и поля поиска. Переменные и параметры выбора можно сочетать с ними тремя способами: поиск по данным, сравнение с данными и сравнение по логике.

Поиск по значениям данных

Активные значения виджета вместе с параметром selection позволяют найти точки с совпадающими значениями в наборе данных. Например, свяжем список с точечным выбором, чтобы выделять автомобили заданного Origin:

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
)

Хотя в списке с начала отображается выбранное значение, для реального начального выбора на диаграмме нужно задать value=. Это тот же подход, что и для переменных; пример приведён ниже в привязке канала кодирования.

Диаграмма по-прежнему реагирует на выбор через условия, как и без виджетов. Привязки позволяют также фильтровать данные, оставляя только выбранные точки.

В следующем примере добавлен пустой выбор, возвращающий все точки после выбора через переключатель или список, где нельзя просто отменить выбранный вариант:

# 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
)

Через общую функцию binding доступны и другие HTML-виджеты. Ниже поле поиска выделяет точки с точным совпадением строки. Наведите указатель на точку, посмотрите название автомобиля и введите его целиком, например vw pickup:

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
)

Точное совпадение не всегда удобно. В разделе выражений показан поиск по частичной строке через регулярное выражение.

Сравнение с данными

Выборы удобны для поиска точного совпадения, но иногда нужно сравнение: например, выделить точки выше или ниже порога ползунка. Для этого рекомендуется переменная через param.

Синтаксис datum.xval обращается к столбцу данных. Префикс datum сообщает Altair, что сравнивается поле DataFrame, а не переменная Python xval, как в записи xval < selector:

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
)

В этом случае можно использовать и параметр выбора: его значения доступны выражениям. Вот ползунок порога и цвет в зависимости от того, ниже или выше него находится точка:

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
)

Знать способ доступа к значениям выбора полезно, но для простых взаимодействий переменные параметров, введённые в Altair 5, часто удобнее. Они также доступны в строках выражений.

Для поиска точного значения можно использовать равенство, например alt.datum.xval == selector, но часто удобнее параметр выбора с явно указанным полем или кодированием.

Сравнение по логике

Логическое сравнение основано на правилах и условиях, а не на самих значениях данных. Например, для флажка нужно проверить, отмечен он или нет, и выполнить действие.

В таком переключателе используется param, а не selection_point: нас интересует состояние флажка True/False, а не наличие этих значений в данных:

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
)

Другой виджет, независимый от данных, — выбор цвета через общую функцию binding. Здесь пользователь интерактивно задаёт цвета диаграммы:

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
)

Привязка легенды

Интерактивная легенда помогает сосредоточиться на группах данных. Вместо самостоятельного построения отдельной диаграммы-легенды Altair предоставляет bind='legend' для легенды, элементы которой выбираются щелчком:

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
)

Привязка шкал

Для интервального выбора bind="scales" автоматически связывает выбор с перемещением и масштабированием диаграммы:

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
)

Это распространённый подход, поэтому метод interactive предоставляет более короткую запись:

alt.Chart(cars).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color='Origin:N',
).interactive()

Привязка канала кодирования

Чтобы выбор виджета менял отображаемые столбцы, нужна связь с каналом кодирования. В отличие от легенд и шкал, задать её при создании выбора, например через bind='x', нельзя.

Значение параметра можно передать каналу. Это гибче, но пока issue #7365 не решён, нужно отдельное вычисляемое преобразование.

В примере значение параметра доступно по имени. Индексирование данных через datum[] извлекает выбранный столбец и передаёт его значения каналу x:

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
)

Параметры внутри calculate позволяют задавать динамические вычисления, например вычитать разные пары столбцов. См. интерактивный выбор столбца. Там заголовок диаграммы тоже обновляется выражением с параметром; подробнее см. доступ к значениям параметров.

Сейчас заголовки осей нельзя динамически менять по выбранному параметру. Пока issue #7264 не решён, можно вместо заголовка использовать текстовый примитив; такой подход показан в ответе Stack Overflow «Can I turn Altair axis titles into links?».