Основы статистической визуализации
Этот урок адаптирован из вводного урока Vega-Lite.
Здесь мы пройдём основные шаги создания визуализаций в Altair. Сначала убедитесь, что пакет Altair и его зависимости установлены.
Предполагается, что вы работаете в интерфейсе блокнотов Jupyter — например, JupyterLab, Colab или VS Code, — где диаграммы отображаются автоматически. Если вы используете другую среду, сначала прочитайте об отображении диаграмм Altair.
Содержание урока:
- Данные.
- Кодирование и графические примитивы.
- Агрегирование данных.
- Настройка визуализации.
- Публикация визуализации.
Данные
Работа с данными в Altair строится вокруг pandas DataFrame. Одна из особенностей статистической визуализации — использование упорядоченных (tidy) таблиц данных. Для этого урока импортируем pandas и создадим простой DataFrame: столбец a содержит категориальную переменную, а b — числовую.
import pandas as pd
data = pd.DataFrame({'a': list('CCCDDDEEE'),
'b': [2, 7, 4, 1, 2, 6, 8, 4, 7]})
Чаще всего данные передают в Altair в виде DataFrame. Имена столбцов таблицы играют важную роль при построении диаграмм, как мы увидим далее.
Объект Chart
Основной объект Altair — Chart. При его создании DataFrame передаётся единственным аргументом:
import altair as alt
chart = alt.Chart(data)
Объект диаграммы уже создан, но мы ещё не указали, что он должен делать с данными. Это следующий шаг.
Кодирование и графические примитивы
Теперь зададим, как нужно показывать данные. Для этого используется атрибут mark объекта диаграммы, с которым удобнее всего работать через методы Chart.mark_*. Например, чтобы представить данные точками, вызовем Chart.mark_point:
alt.Chart(data).mark_point()
Каждой строке набора данных соответствует точка. Все точки находятся друг над другом, поскольку мы ещё не задали их положения.
Чтобы разделить точки визуально, свяжем столбцы данных с каналами кодирования, или просто каналами. Например, переменную a можно закодировать каналом x, который определяет положение точки по оси x. Для этого используется метод Chart.encode:
alt.Chart(data).mark_point().encode(
x='a',
)
Метод encode() задаёт соответствия между каналами — x, y, color, shape, size и другими — и столбцами набора данных, указанными по имени.
При работе с pandas DataFrame Altair автоматически определяет тип данных столбца. В этом примере он номинальный: значения представляют категории без определённого порядка.
Мы разделили точки по одному признаку, но внутри каждой категории они по-прежнему перекрываются. Добавим канал y и свяжем его со столбцом b:
alt.Chart(data).mark_point().encode(
x='a',
y='b'
)
Altair также автоматически определяет тип столбца b: теперь он количественный, то есть содержит числовые значения. Линии сетки и подходящие заголовки осей тоже добавляются автоматически.
Преобразование данных: агрегирование
Altair содержит встроенный синтаксис агрегирования, который позволяет гибче представлять данные. Например, среднее значение можно вычислить, указав операцию агрегирования внутри обозначения столбца:
alt.Chart(data).mark_point().encode(
x='a',
y='average(b)'
)
Теперь каждой категории по оси x соответствует одна точка, показывающая среднее значение внутри категории.
Агрегированные значения часто показывают столбцами. Заменим Chart.mark_point на Chart.mark_bar:
alt.Chart(data).mark_bar().encode(
x='a',
y='average(b)'
)
Категориальный признак связан с осью x, поэтому получилась диаграмма с вертикальными столбцами. Для горизонтальной достаточно поменять местами аргументы x и y:
alt.Chart(data).mark_bar().encode(
y='a',
x='average(b)'
)
Отступление: просмотр JSON
Основная задача Altair — преобразовать описание диаграммы в строку JSON, соответствующую схеме Vega-Lite. С помощью метода Chart.to_json можно посмотреть спецификацию, которую Altair формирует и передаёт в Vega-Lite:
chart = alt.Chart(data).mark_bar().encode(
x='a',
y='average(b)',
)
print(chart.to_json())
Сокращённая запись encode(x='a') разворачивается в JSON-структуру с именем поля field и типом данных type. Запись для канала y разворачивается аналогично и дополнительно содержит свойство aggregate.
Полный сокращённый синтаксис Altair также позволяет явно указать тип столбца:
y = alt.Y('average(b):Q')
print(y.to_json())
Это эквивалентно записи с именованными параметрами:
y = alt.Y(field='b', type='quantitative', aggregate='average')
print(y.to_json())
Такой подробный способ задания каналов можно использовать непосредственно в спецификации диаграммы Altair. Это полезно при работе с более сложными настройками полей:
alt.Chart(data).mark_bar().encode(
alt.Y('a', type='nominal'),
alt.X('b', type='quantitative', aggregate='average')
)
Настройка визуализации
По умолчанию Altair и Vega-Lite самостоятельно выбирают некоторые свойства диаграммы. API Altair позволяет изменить её оформление. Например, заголовки осей можно задать методом title классов каналов, а цвет примитивов — аргументом color метода Chart.mark_*, передав допустимую строку цвета HTML:
alt.Chart(data).mark_bar(color='firebrick').encode(
alt.Y('a').title('категория'),
alt.X('average(b)').title('среднее b по категориям')
)
Публикация визуализации
Готовую визуализацию можно опубликовать в интернете с помощью JavaScript-пакета Vega-Embed. Для любой диаграммы метод Chart.save создаёт самостоятельный HTML-документ:
chart = alt.Chart(data).mark_bar().encode(
x='a',
y='average(b)',
)
chart.save('chart.html')
Базовый HTML-шаблон имеет следующий вид. JSON-спецификацию, полученную с помощью Chart.to_json, нужно поместить в JavaScript-переменную spec:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vega@3"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@2"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@3"></script>
</head>
<body>
<div id="vis"></div>
<script type="text/javascript">
var spec = {}; /* JSON-спецификация диаграммы */
var opt = {"renderer": "canvas", "actions": false}; /* Настройки встраивания */
vegaEmbed("#vis", spec, opt);
</script>
</body>
</html>
Метод Chart.save позволяет удобно сохранить такой HTML в файл. Подробнее о встраивании Altair и Vega-Lite рассказано в документации проекта Vega-Embed.