2025, Nov 01 19:16

Сортировка оси X с mark_errorbar в Altair: решение через EncodingSortField

Почему сортировка оси X в многослойных диаграммах Altair с mark_errorbar сбивается и как это исправить. Показываем рабочий прием с EncodingSortField.

Слоистые диаграммы в Altair — один из базовых приемов, когда нужно объединить столбцы с интервалами или полосами неопределенности. Частая задача — упорядочить ось X по количественному показателю на оси Y. Для столбцов и правил это просто, но с появлением интервалов ошибок начинается путаница: та же сортировка неожиданно возвращается к алфавитному порядку.

Воспроизводим проблему

Пример ниже иллюстрирует столбчатую диаграмму, поверх которой накладываются либо линии-правила, либо интервалы ошибок. Сортировка оси X по метрике Y работает с правилами, но не с интервалами ошибок — хотя каждый слой по отдельности сортируется верно.

import polars as pl
import altair as alt
data_tbl = pl.DataFrame({
    "label": ["A", "B", "C", "D"],
    "avg": [1.2, 2.5, 0.9, 3.1],
    "lo": [1.0, 2.2, 0.85, 2.8],
    "hi": [1.4, 2.8, 0.95, 3.4]
})
base_cols = alt.Chart(data_tbl).mark_bar().encode(
    x=alt.X("label:N").sort("-y"),
    y=alt.Y("avg:Q")
)
err_overlay = base_cols.mark_errorbar().encode(
    y="lo:Q",
    y2="hi:Q"
)
rule_overlay = base_cols.mark_rule().encode(
    y="lo:Q",
    y2="hi:Q"
)
# Не сортируется
base_cols + err_overlay
# Сортируется
base_cols + rule_overlay

Каждый слой по отдельности сортируется корректно. В комбинировании «столбцы + интервалы ошибок» ось X возвращается к алфавитному порядку, тогда как «столбцы + правила» сохраняют сортировку по Y.

Что происходит

Поведение сводится к способу задания сортировки. Короткая строковая запись сортировки по X с помощью «-y» при наложении слоёв с интервальными ошибками может игнорироваться, тогда как при наложении с правилами она работает. На практике это означает, что неявная сортировка по каналу Y для mark_errorbar в многослойной диаграмме соблюдается не всегда. Явная сортировка по полю устраняет несогласованность.

Как исправить

Задайте сортировку через EncodingSortField на канале X и укажите поле с количественной метрикой, которое должно определять порядок. Тогда порядок сохранится и при наложении интервалов ошибок.

import polars as pl
import altair as alt
data_tbl = pl.DataFrame({
    "label": ["A", "B", "C", "D"],
    "avg": [1.2, 2.5, 0.9, 3.1],
    "lo": [1.0, 2.2, 0.85, 2.8],
    "hi": [1.4, 2.8, 0.95, 3.4]
})
base_cols = alt.Chart(data_tbl).mark_bar().encode(
    x=alt.X("label:N").sort(alt.EncodingSortField(field="avg", op="min")),
    y=alt.Y("avg:Q")
)
err_overlay = base_cols.mark_errorbar().encode(
    y="lo:Q",
    y2="hi:Q"
)
# Теперь сортируется правильно
base_cols + err_overlay

Подробнее: issue на GitHub.

Почему это важно

Порядок на осях — не косметика. От него зависит, как считываются ранжирования, экстремумы и общие закономерности. При добавлении неопределенности через mark_errorbar сокращённая запись сортировки может незаметно скатиться к алфавитному порядку, что мешает сравнению. Явный EncodingSortField привязывает порядок к нужной метрике и делает диаграмму устойчивой к тонкостям наложения слоёв.

Выводы

Если нужно отсортировать ось X в многослойной диаграмме по величине, зависящей от Y, и вы добавляете интервалы ошибок, отдавайте предпочтение явной сортировке через EncodingSortField на канале X. Укажите количественное поле, определяющее высоту столбцов, и задайте агрегацию, соответствующую замыслу. Эта маленькая правка предотвращает неожиданную перестановку и сохраняет согласованность визуализаций.

Материал основан на вопросе на StackOverflow от sasha и ответе kgoodrick.