Внутреннее устройство Altair
Этот раздел объясняет, как API Altair связан со спецификацией визуализации Vega-Lite и как эти знания помогают эффективнее пользоваться библиотекой.
Прежде всего, сам Altair не отрисовывает визуализации. Его основная задача точно определена:
- Altair предоставляет Python API для создания спецификаций Vega-Lite с проверкой их корректности.
Чтобы превратить спецификацию в визуализацию, нужен правильно настроенный интерфейс отображения. Строго говоря, отрисовка обычно не управляется самим пакетом Altair.
Из диаграммы Altair в спецификацию Vega-Lite
Поскольку Altair создаёт спецификации диаграмм, основные методы каждого объекта — Chart.to_dict и Chart.to_json. Они возвращают спецификацию как словарь Python или строку JSON соответственно. Например, создадим простую диаграмму рассеяния и выведем JSON:
import altair as alt
from altair.datasets import data
chart = alt.Chart(data.cars.url).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N',
).configure_view(
continuousHeight=300,
continuousWidth=300,
)
print(chart.to_json(indent=2))
Перед возвратом словаря или JSON Altair проверяет результат по схеме Vega-Lite с помощью пакета jsonschema. Схема задаёт допустимые атрибуты и значения спецификации диаграммы.
Полученную JSON-спецификацию можно передать библиотеке, например Vega-Embed, которая умеет прочитать её и отрисовать описанную диаграмму:
При работе в JupyterLab, Jupyter Notebook и других интерфейсах их расширения извлекают JSON из объекта диаграммы Altair и передают его соответствующему коду отрисовки.
Низкоуровневая структура объектов Altair
Обычные методы API Altair — Chart.mark_point, Chart.encode, configure_*(), transform_*() и другие — это удобные высокоуровневые функции, оборачивающие низкоуровневый API. Последний представляет собой иерархию объектов Python, соответствующую иерархии определения JSON-схемы.
Например, вместо удобных методов можно построить предыдущую диаграмму напрямую из низкоуровневых объектов:
alt.Chart(
data=alt.UrlData(
url='https://vega.github.io/vega-datasets/data/cars.json'
),
mark='point',
encoding=alt.FacetedEncoding(
x=alt.PositionFieldDef(
field='Horsepower',
type='quantitative'
),
y=alt.PositionFieldDef(
field='Miles_per_Gallon',
type='quantitative'
),
color=alt.StringFieldDefWithCondition(
field='Origin',
type='nominal'
)
),
config=alt.Config(
view=alt.ViewConfig(
continuousHeight=300,
continuousWidth=300
)
)
)
Запись гораздо длиннее обычного кода Altair, но яснее показывает соответствие между структурой объектов Python и структурой схемы Vega-Lite.
Эта иерархия не создаётся вручную: она генерируется программно из схемы Vega-Lite скриптом generate_schema_wrapper.py из репозитория Altair.
При генерации описания из схемы Vega-Lite попадают в строки документации классов Python, по которым затем автоматически создаётся справочник API. Благодаря этому Altair можно быстро обновлять вслед за схемой Vega-Lite; вручную достаточно обновить высокоуровневые методы диаграмм.
Преобразование Vega-Lite в Altair
С этими знаниями и небольшой практикой построить диаграмму Altair по спецификации Vega-Lite довольно просто. Рассмотрим простую столбчатую диаграмму из документации Vega-Lite со следующей JSON-спецификацией:
{
"$schema": "https://vega.github.io/schema/vega-lite/v6.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
]
},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
}
На самом низком уровне метод класса Chart.from_json создаёт объект диаграммы Altair из строки JSON Vega-Lite:
import altair as alt
alt.Chart.from_json("""
{
"$schema": "https://vega.github.io/schema/vega-lite/v6.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
]
},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
}
""")
Если вместо строки у вас соответствующий словарь Python, используйте Chart.from_dict:
import altair as alt
alt.Chart.from_dict({
"$schema": "https://vega.github.io/schema/vega-lite/v6.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
]
},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
})
С небольшими дополнительными усилиями и аккуратным копированием можно вручную переписать эту спецификацию в более привычный код Altair, в том числе создав DataFrame pandas из значений:
import altair as alt
import pandas as pd
data = pd.DataFrame.from_records([
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
])
alt.Chart(data).mark_bar().encode(
x='a:O',
y='b:Q'
)
Важно понимать, что свойства "encoding" обычно задаются методом Chart.encode, типы кодирования выводятся из сокращённых кодов типов, а свойства "transform" и "config" соответствуют методам transform_*() и configure_*().
Таким способом участники проекта создали многие первые примеры галереи Altair, опираясь на галерею Vega-Lite. Знание соответствий между Altair и Vega-Lite помогает использовать документацию Vega-Lite там, где материалы Altair недостаточно подробны или неполны.