2025, Oct 19 02:31
Один столбец на строку в Altair при повторе категорий X
Поясняем, почему Altair объединяет столбцы при повторе категорий по X, и показываем решение: уникальный ключ по оси X плюс labelExpr для исходных подписей.
Altair группирует строки, относящиеся к одной и той же категории по оси X. Если значение поля повторяется, его значения по оси Y суммируются, и в итоге столбцов становится меньше, чем строк. Когда в исходных данных дубликаты осознанны и вам нужен один столбец на строку, потребуется уникальный ключ для канала x — при этом на оси должны оставаться исходные подписи.
Воспроизведение проблемы
Набор данных содержит четыре строки, при этом два элемента имеют одинаковое значение в поле a. Прямое кодирование Altair сворачивает их в три столбца, потому что обе строки с B складываются.
data_tbl = pd.DataFrame({
    'a': ['A', 'B', 'C', 'B'],
    'b': [10, 20, 30, 15]
})
alt.Chart(data_tbl).mark_bar().encode(
    x='a',
    y='b'
)
Для сравнения, построение с pandas рисует по одному столбцу на строку, поскольку рассматривает каждую запись независимо, даже когда подписи повторяются.
data_tbl.plot(kind='bar', x='a', y='b')
Почему столбцов меньше
В такой конфигурации строки с одинаковым значением a объединяются, поэтому две строки с B складываются в один столбец со значением 35. В результате получается три столбца для A, B и C, а не четыре, соответствующие числу строк.
Наивный обходной путь и его ограничения
Использование индекса DataFrame в качестве канала x дает четыре столбца, но подписи превращаются в числовые индексы, и исходные значения a на оси теряются.
alt.Chart(data_tbl.reset_index()).mark_bar().encode(
    x='index:O',
    y='b'
)
Это сохраняет количество столбцов, но лишает нужных категориальных меток.
Аккуратное решение: уникальные ключи по x и исходные подписи
Сформируйте уникальный ключ по x, объединив индекс строки со значением a, а на оси показывайте только часть a. Так вы сохраните соответствие «одна строка — один столбец» и оставите на оси X ожидаемый текст.
alt.Chart(data_tbl.reset_index()).transform_calculate(
    x_token=alt.datum['a'] + '_' + alt.datum['index']
).mark_bar().encode(
    alt.X('x_token:N', title='A', axis=alt.Axis(labelExpr='split(datum.value, "_")[0]')),
    alt.Y('b')
)
Преобразование создает составной ключ, уникальный для каждой строки. Выражение labelExpr на оси разделяет ключ по символу подчеркивания и показывает только левую часть, тем самым возвращая на ось исходные значения a и не допуская агрегации.
Почему это важно
Когда вы осознанно используете повторяющиеся категории для разных записей, их тихое объединение меняет и число элементов на графике, и высоту столбцов. Гарантируя соответствие «строка ↔ отметка», вы получаете визуализацию, которая отражает исходные данные без нежелательной консолидации. Одновременно сохранение исходных подписей поддерживает читаемость и соответствует смыслу данных.
Выводы
Если вы ожидаете по одному столбцу на строку, но категории повторяются, создайте уникальный ключ канала x и настройте подписи оси так, чтобы показывались исходные категории. Такой подход исключает агрегацию, оставляет число столбцов равным числу строк и сохраняет информативные подписи.