JupyterChart
Класс JupyterChart, появившийся в Vega-Altair 5.1, позволяет обновлять уже отображённые диаграммы и получать из Python состояние интерактивных параметров.
Поддерживаемые среды
JupyterChart — виджет Jupyter, созданный на основе библиотеки AnyWidget. Он совместим со средами разработки и инструментами для панелей мониторинга, которые поддерживают сторонние виджеты Jupyter. Проверена работа в следующих средах:
- Classic Jupyter Notebook;
- JupyterLab;
- Visual Studio Code;
- Google Colab;
- Voila.
Если вы попробуете
JupyterChartв другой среде с поддержкой виджетов Jupyter, расскажите разработчикам о результате, чтобы они могли обновить этот список.
Основы использования
Для создания JupyterChart передайте обычный экземпляр Chart конструктору alt.JupyterChart. Диаграмма отображается автоматически, если результат последнего выражения в ячейке блокнота — экземпляр JupyterChart:
import altair as alt
import pandas as pd
source = pd.DataFrame({
'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
'b': [28, 55, 43, 91, 81, 53, 19, 87, 52]
})
chart = alt.Chart(source).mark_bar().encode(
x='a',
y='b'
)
jchart = alt.JupyterChart(chart)
jchart
Обновление диаграмм
Свойству chart объекта JupyterChart можно присвоить новый экземпляр диаграммы. Он сразу заменит предыдущую диаграмму:
jchart.chart = chart.mark_bar(color="crimson", cornerRadius=10)
Параметры: переменные и выборы
Как описано в разделе интерактивности, грамматика интерактивности Vega-Altair основана на параметрах. Переменные параметры хранят простое значение, а параметры выбора связывают действия пользователя с запросами к данным.
Класс JupyterChart предоставляет доступ из Python к обоим типам параметров.
Переменные параметры
JupyterChart позволяет получать значения переменных параметров, наблюдать за их изменениями, задавать значения и связывать параметры с другими виджетами.
Доступ к переменным параметрам
Переменные параметры диаграммы хранятся в свойстве params экземпляра JupyterChart. К отдельным именованным параметрам можно обращаться как к обычным атрибутам. В этом примере привязка связывает параметр cutoff с ползунком. Текущее значение доступно через jchart.params.cutoff:
import altair as alt
import pandas as pd
import numpy as np
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)
cutoff = alt.param(name="cutoff", bind=slider, value=50)
predicate = alt.datum.xval < cutoff
chart = alt.Chart(df).mark_point().encode(
x='xval',
y='yval',
color=alt.when(predicate).then(alt.value("red")).otherwise(alt.value("blue")),
).add_params(
cutoff
)
jchart = alt.JupyterChart(chart)
jchart
Наблюдение за переменными параметрами
Метод observe свойства params регистрирует функцию обратного вызова, которая выполняется при изменении параметра. Здесь простая функция выводит значение параметра cutoff:
def on_cutoff_change(change):
print(change.new)
jchart.params.observe(on_cutoff_change, ["cutoff"])
Задание значений переменных параметров
Значение переменного параметра можно изменить из Python, присвоив его соответствующему атрибуту params. Ниже значение cutoff обновляется присваиванием атрибуту jchart.params.cutoff:
Связывание переменных параметров
Поскольку params — объект traitlets, функция link из ipywidgets позволяет связать параметры с другими виджетами ipywidgets. Здесь переменный параметр cutoff связан со значением IntSlider:
from ipywidgets import IntSlider, link
slider = IntSlider(23, min=0, max=100)
link((slider, "value"), (jchart.params, "cutoff"))
slider
Если виджет ipywidgets уже связан с переменным параметром Vega-Altair, дополнительно привязывать этот параметр к виджету Vega-Altair не требуется. В следующем варианте значение cutoff управляется только виджетом IntSlider из ipywidgets:
import pandas as pd
import numpy as np
rand = np.random.RandomState(42)
df = pd.DataFrame({
'xval': range(100),
'yval': rand.randn(100).cumsum()
})
cutoff = alt.param(name="cutoff", value=50)
predicate = alt.datum.xval < cutoff
chart = alt.Chart(df).mark_point().encode(
x='xval',
y='yval',
color=alt.when(predicate).then(alt.value("red")).otherwise(alt.value("blue"))
).add_params(
cutoff
)
jchart = alt.JupyterChart(chart)
jchart
Параметры выбора
JupyterChart позволяет получать параметры выбора и наблюдать за ними. Для доступа из Python выборы делятся на три типа: точечные, индексные и интервальные. Им соответствуют классы Python PointSelection, IndexSelection и IntervalSelection.
Экземпляры этих классов доступны как атрибуты свойства selections объекта JupyterChart.
Точечные выборы
Класс PointSelection хранит текущее состояние точечного выбора Vega-Altair, созданного через alt.selection_point(), если задано fields или encodings. Распространённый пример — выбор с encodings=["color"], привязанный к легенде:
import altair as alt
from altair.datasets import data
source = data.cars()
brush = alt.selection_point(name="point", encodings=["color"], bind="legend")
chart = alt.Chart(source).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.when(brush).then("Origin:N").otherwise(alt.value("grey")),
).add_params(brush)
jchart = alt.JupyterChart(chart)
jchart
Экземпляр PointSelection доступен через jchart.selections.point, где point — значение аргумента name функции alt.selection_point.
Свойство jchart.selections.point.value содержит список словарей: каждый элемент описывает одну выбранную точку. Этот список можно преобразовать в строку запроса pandas query:
filter = " or ".join([
" and ".join([
f"`{col}` == {repr(val)}" for col, val in sel.items()
])
for sel in jchart.selections.point.value
])
source.query(filter)
Например, при выборе элементов легенды Japan и Europe строка filter принимает значение "`Origin` == 'Japan' or `Origin` == 'Europe'". Выражение source.query(filter) возвращает DataFrame pandas со строками исходного набора source, которые входят в выбор.
Индексные выборы
Класс IndexSelection хранит текущее состояние точечного выбора Vega-Altair, созданного через alt.selection_point(), если не заданы ни fields, ни encodings. В этом случае свойство value содержит список индексов выбранных строк. Эти индексы можно передать атрибуту iloc объекта DataFrame pandas, чтобы получить выбранные строки исходного набора данных:
import altair as alt
from altair.datasets import data
source = data.cars()
brush = alt.selection_point(name="point")
chart = alt.Chart(source).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.when(brush).then("Origin:N").otherwise(alt.value("grey")),
).add_params(brush)
jchart = alt.JupyterChart(chart)
jchart
Внимание. Возвращаемые индексы соответствуют исходному DataFrame только в диаграммах без агрегации. Если диаграмма содержит агрегацию, задайте в
alt.selection_pointаргументfieldsилиencodings. ТогдаJupyterChartбудет содержатьPointSelection, а неIndexSelection.
Интервальные выборы
Класс IntervalSelection хранит текущее состояние интервального выбора Vega-Altair, созданного через alt.selection_interval(). Свойство value содержит словарь, сопоставляющий имена столбцов с интервалами выбора:
import altair as alt
from altair.datasets import data
source = data.cars()
brush = alt.selection_interval(name="interval")
chart = alt.Chart(source).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.when(brush).then("Cylinders:O").otherwise(alt.value("grey")),
).add_params(brush)
jchart = alt.JupyterChart(chart)
jchart
Словарь выбора можно преобразовать в строку запроса pandas query:
filter = " and ".join([
f"{v[0]} <= `{k}` <= {v[1]}"
for k, v in jchart.selections.interval.value.items()
])
source.query(filter)
Например, при выборе по оси x от 120 до 160 и по оси y от 25 до 35 свойство jchart.selections.interval.value принимает значение {'Horsepower': [120, 160], 'Miles_per_Gallon': [25, 35]}. Строка filter будет равна "120 <= `Horsepower` <= 160 and 25 <= `Miles_per_Gallon` <= 35". Выражение source.query(filter) возвращает DataFrame pandas со строками source, которые входят в выбор.
Наблюдение за выборами
Как и для переменных параметров, метод observe свойства selections регистрирует функцию обратного вызова, выполняемую при изменении выбора. В этом примере функция реагирует на изменения интервального выбора, фильтрует исходный DataFrame по его значениям и отображает таблицу в HTML. Виджет VBox из ipywidgets располагает диаграмму и таблицу друг под другом:
import ipywidgets
from IPython.display import display
from ipywidgets import HTML, VBox
import altair as alt
from altair.datasets import data
source = data.cars()
brush = alt.selection_interval(name="brush")
chart_widget = alt.JupyterChart(alt.Chart(source).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.when(brush).then("Cylinders:O").otherwise(alt.value("grey")),
).add_params(brush))
table_widget = HTML(value=source.iloc[:0].to_html())
def on_select(change):
sel = change.new.value
if sel is None or 'Horsepower' not in sel:
filtered = source.iloc[:0]
else:
filter_query = (
f"{sel['Horsepower'][0]} <= `Horsepower` <= {sel['Horsepower'][1]} and "
f"{sel['Miles_per_Gallon'][0]} <= `Miles_per_Gallon` <= {sel['Miles_per_Gallon'][1]}"
)
filtered = source.query(filter_query)
table_widget.value = filtered.to_html()
chart_widget.selections.observe(on_select, ["brush"])
VBox([chart_widget, table_widget])
Использование без подключения к интернету
По умолчанию виджет JupyterChart динамически загружает зависимости JavaScript из CDN, поэтому ему требуется интернет. Начиная с Altair 5.3, JupyterChart может загружать эти зависимости из пакета vl-convert-python, что позволяет работать без подключения к сети.
Автономный режим включается методом класса JupyterChart.enable_offline:
import altair as alt
alt.JupyterChart.enable_offline()
Достаточно вызвать его один раз: после этого все отображаемые объекты JupyterChart будут работать в автономном режиме.
Для отключения этого режима передайте тому же методу offline=False:
import altair as alt
alt.JupyterChart.enable_offline(offline=False)
Ограничения
Задание состояния выбора
В настоящее время задавать состояние выбора из Python нельзя.