Столбчатые диаграммы

Bar — рабочая лошадь визуализации. Длина столбика — самый честный способ сравнить категории.

«Если сомневаетесь, какой график взять для сравнения — берите столбчатую диаграмму. Ошибиться почти невозможно».

Столбчатая диаграмма сравнивает значения по категориям: выручка по регионам, число жалоб по отделам, доля рынка по продуктам. Категория ложится на одну ось, число — на другую, а длина столбика кодирует величину. Поскольку длина декодируется человеком точно, bar — самый надёжный выбор для сравнения.

За кажущейся простотой bar скрывается его главная сила: это самый «защищённый от обмана» график при правильной оси. Длина читается от общей базовой линии, и зритель сравнивает столбики почти без усилия и почти без ошибки — именно поэтому в деловой аналитике bar встречается чаще всех остальных типов вместе взятых. Но та же опора на базовую линию делает bar и самым уязвимым к манипуляции: стоит обрезать ось снизу, и безобидная разница в пару процентов превращается в визуально «двукратный отрыв». Поэтому для столбчатой диаграммы действует жёсткое правило, которого нет для линейного графика: ось значений обязана начинаться с нуля.

Полезно различать три родственные формы. Простой bar сравнивает категории по одному показателю. Группированный (grouped) ставит рядом несколько серий — план и факт, мужчины и женщины — и хорош, когда серий две-три и важно сравнение внутри каждой категории. Накопленный (stacked) кладёт сегменты друг на друга и показывает, как целое делится на части; его частный случай — 100%-stacked, где все столбики одной высоты и сравнивается только структура, а не объём. Выбор между ними диктует вопрос: «сравнить серии» — группированный, «показать состав» — накопленный.

# Вертикальные столбики
fig, ax = plt.subplots()
ax.bar(regions, sales)
ax.set_ylabel("Продажи, шт")

# Горизонтальные — удобны для длинных подписей
fig, ax = plt.subplots()
ax.barh(regions, sales)

# Группированные столбики (две серии рядом)
import numpy as np
x = np.arange(len(regions))
w = 0.4
ax.bar(x - w/2, plan, w, label="План")
ax.bar(x + w/2, fact, w, label="Факт")
ax.set_xticks(x); ax.set_xticklabels(regions)
ax.legend()

Как работает под капотом

Каждый столбик — прямоугольник-artist с координатой основания и высотой = значению. Группированные столбики — это сдвиг позиции каждой серии на полширины. Накопленные (stacked) — каждый следующий столбик стартует с вершины предыдущего (параметр bottom=). Поэтому важно: высота читается от основания, и если основание не ноль — глаз обманут.

Перед построением данные почти всегда сортируют и иногда агрегируют. Сделаем это руками.

# Агрегация и сортировка категорий для bar chart
rows = [("Север", 120), ("Юг", 90), ("Север", 60),
        ("Запад", 200), ("Юг", 110), ("Запад", 50)]

agg = {}
for region, val in rows:
    agg[region] = agg.get(region, 0) + val

# сортируем по убыванию — так bar читается лучше всего
order = sorted(agg.items(), key=lambda kv: kv[1], reverse=True)
for region, total in order:
    print("{:<6} {:>4} {}".format(region, total, "#" * (total // 10)))

«Попробуй сам ▶» — отсортированные суммы и есть готовые данные для ax.bar. Сортировка по величине — половина успеха читаемого bar.

Частые ошибки

Главная и опасная: обрезанная ось Y (не от нуля) — превращает разницу в 3% в видимую разницу в 3 раза. Несортированные категории — глаз сам ищет порядок. Слишком много столбиков (десятки) — лучше barh или топ-N. 3D-столбики — искажают высоту.

Есть и менее очевидные промахи. Накопленный bar с пятью-шестью сегментами читается плохо: точно сравнить можно только нижний сегмент (у него общая база), а у верхних базовая линия «плавает», и сравнить их между категориями почти невозможно — если важны именно верхние части, лучше разнести их в отдельные группированные столбики или small multiples. Радужная или слишком яркая заливка каждого столбика разным цветом без смысла перегружает график: если все столбики — одна сущность, делайте их одного цвета, а цветом выделяйте лишь один акцентный. Подписи значений над каждым столбиком при их большом числе превращаются в кашу — подписывайте выборочно. Наконец, при сравнении категорий с очень разными порядками величин линейная ось «придавливает» мелкие к нулю; иногда уместна логарифмическая ось, но тогда об этом нужно явно предупредить читателя, потому что длина на ней уже не пропорциональна величине.

Best practices

  • Ось значений всегда от нуля — это нерушимое правило для bar.
  • Сортируйте по величине (если нет естественного порядка).
  • Длинные подписи — горизонтальные столбики (barh).
  • Не более 2–3 серий в группированных столбиках.

Итог: bar — безопасный выбор для сравнения. Дальше — линейные графики для динамики во времени.

Проверьте себя
1. Почему ось значений на bar chart должна начинаться с нуля?
AТак быстрее рисуется
BДлина столбика кодирует величину; ненулевое основание искажает сравнение
CИначе легенда не работает
DЭто требование Matplotlib
2. Что лучше сделать с категориями на bar chart, если у них нет естественного порядка?
AОставить как пришли из данных
BОтсортировать по величине
CРасположить по алфавиту всегда
DПеремешать случайно