Визуализация в Python: как строить понятные графики в matplotlib и seaborn
Как выбрать тип графика и собрать его в Python: линии, столбцы, распределения и сравнение сегментов без декоративного шума.
Содержание статьи
График не должен показывать всё, что есть в таблице. Его задача — помочь увидеть один паттерн: тренд, разницу между сегментами, распределение или выброс. В Python удобство matplotlib и seaborn начинается не с выбора цвета, а с вопроса, на который должна ответить визуализация.
Коротко
Линия показывает изменение во времени, столбцы сравнивают категории, histogram показывает распределение, boxplot помогает увидеть медиану и выбросы. Seaborn даёт удобные статистические шаблоны, matplotlib — полный контроль над композицией и подписями.
- Один график — один главный вывод.
- Подписывай единицу измерения, период и знаменатель.
- Сортируй категории по смыслу, а не по случайному порядку в файле.
- Цвет должен разделять смысл, а не просто украшать холст.
Сначала выбери форму вопроса
Если команда спрашивает “как меняется DAU по дням”, нужен line chart. Если “какой канал принёс больше заказов”, подойдут горизонтальные bars. Для “есть ли длинный хвост чеков” нужен histogram. Неподходящий тип графика может сделать правильные данные трудными для чтения.
| Вопрос | График | На что смотреть |
|---|---|---|
| Как меняется метрика во времени? | line | тренд, сезонность, перелом |
| Какие сегменты отличаются? | bar | уровень и порядок категорий |
| Как распределены значения? | histogram | центр, хвост, выбросы |
| Есть ли связь двух чисел? | scatter | направление и необычные точки |
| Как ведут себя когорты? | heatmap | градиент и провалы по возрасту |
Линия для тренда
Перед построением линии отсортируй даты и приведи данные к одному уровню: например, один день и одна строка на канал. Если на одном дне несколько строк, matplotlib соединит их в порядок чтения DataFrame, и график может выглядеть как случайная пила.
import matplotlib.pyplot as plt
daily = (
events.groupby('event_date', as_index=False)
.agg(dau=('user_id', 'nunique'))
.sort_values('event_date')
)
fig, ax = plt.subplots(figsize=(9, 4))
ax.plot(daily['event_date'], daily['dau'], color='#6d5dfc', linewidth=2)
ax.set(title='DAU by day', xlabel='Date', ylabel='Unique active users')
ax.grid(axis='y', alpha=0.2)
fig.tight_layout()Сравнение сегментов через seaborn
Seaborn удобен, когда данные уже находятся в “длинном” формате: отдельные колонки для даты, сегмента и значения. Он сам создаёт легенду и даёт аккуратные статистические шаблоны. Но подписи и порядок категорий всё равно нужно задать самостоятельно.
Для бизнес-решения рядом нужны количество заказов и доверительный диапазон, а не только среднее.
import seaborn as sns
import matplotlib.pyplot as plt
channel_avg = orders.groupby('channel', as_index=False).agg(
average_order=('revenue', 'mean'),
)
channel_avg = channel_avg.sort_values('average_order')
fig, ax = plt.subplots(figsize=(8, 4))
sns.barplot(data=channel_avg, x='average_order', y='channel', color='#0f9f76', ax=ax)
ax.set(xlabel='Average order value, RUB', ylabel='Channel')
fig.tight_layout()Почему красивый график может врать
Обрезанная ось превращает маленькую разницу в драматичный разрыв. Среднее скрывает несколько крупных заказов. Сглаженная линия создаёт впечатление причинности. График помогает сформировать вопрос, но не отвечает на него без определения метрики, размера базы и проверки сегментов.
После визуального сигнала вернись к зерну, знаменателю и статистической неопределённости. Особенно если график должен привести к продуктовой или финансовой рекомендации.
- Показывай размер сегмента рядом с процентом или средним.
- Для ratio-метрик не смешивай среднее по пользователям с отношением сумм.
- Не используй двойную ось без крайней необходимости.
- Отмечай неполный текущий период.
- Сохраняй исходную таблицу, из которой построен график.
От вопроса к форме графика
Начинай не с выбора функции matplotlib, а с вопроса. Если нужно увидеть изменение во времени, нужна линия с одинаковым шагом периода. Если нужно сравнить категории, подойдут горизонтальные столбцы, отсортированные по показателю. Если вопрос про распределение чеков, среднее и один bar chart скроют форму — нужны histogram, boxplot или квантили.
Один график должен отвечать на одну основную задачу. Не помещай на одну ось DAU, revenue, конверсию и количество заказов только потому, что так помещается больше информации. Лучше сделать небольшой набор согласованных графиков: общий тренд, разрез по сегменту и таблицу с базой. Читателю проще связать три ясных объекта, чем расшифровывать один перегруженный.
Пиши подпись как вывод о том, что нужно проверить: «DAU снизился после 15 августа, но падение видно только на Android». Такая подпись направляет чтение, но не подменяет анализ. В примечании укажи период, размер базы и определение активного пользователя.
| Вопрос | График | Обязательная оговорка |
|---|---|---|
| как меняется метрика | линия по времени | неполный текущий период |
| какой сегмент больше | отсортированные bars | размер базы и единицы |
| где теряются пользователи | воронка | порядок и окно событий |
| как распределены значения | histogram или boxplot | выбросы и размер выборки |
| как ведут себя когорты | heatmap | возраст когорты и знаменатель |
Подготовь таблицу до вызова seaborn
График не должен сам решать, что считать. Сначала собери DataFrame на нужном зерне: дата, сегмент, значение, база и, если нужно, доверительный интервал. Не передавай в визуализацию сырые события, если задача — сравнить дневной DAU. Иначе библиотека посчитает частоты строк, а не смысловую метрику.
Для категорий с длинными названиями используй горизонтальные bars и явно сортируй результат. Для временных рядов отсортируй дату и добавь пропущенные периоды, если отсутствие строки означает нулевое наблюдение. Но не заполняй нулём период, для которого данные ещё не загрузились. В отчёте нужно различать «значение равно нулю» и «источник не прислал день».
Перед рендером проверь единицы: рубли, тысячи рублей, проценты или доли. Подпись оси и форматирование чисел должны соответствовать данным. Ошибка в единицах визуально выглядит профессионально, поэтому её ловит только отдельная проверка.
daily = (
events.assign(event_date=pd.to_datetime(events['occurred_at']).dt.date)
.groupby('event_date', as_index=False)
.agg(dau=('user_id', 'nunique'))
.sort_values('event_date')
)
ax = daily.plot(x='event_date', y='dau', marker='o')
ax.set(xlabel='Дата', ylabel='Уникальные пользователи')Неопределённость и размер базы должны быть видны
Процент без знаменателя почти бесполезен. На графике конверсия 40% может относиться к 20 пользователям, а конверсия 8% — к 50 000. Показывай рядом размер базы или подпиши его в таблице. Для сравнения групп добавляй доверительный интервал или хотя бы количество наблюдений, если точная статистика не входит в задачу.
Сглаживание и rolling mean помогают увидеть тренд, но скрывают короткие скачки. Показывай исходную линию тоньше и подписывай окно сглаживания. Иначе команда может принять задержанный показатель за сегодняшнюю реакцию продукта.
Неполный день или неделя должны иметь отдельный визуальный статус. Серый фон, пунктир или подпись «данные по 13:00» лучше, чем сравнение незавершённого периода с полным. Это особенно важно для revenue и DAU, которые естественно растут внутри дня.
Покажи период, единицы, базу и определение. Если есть фильтр, назови его. Читатель должен понять график без поиска по пяти ячейкам ноутбука.
Практика: один график для продуктового решения
Возьми падение метрики и сначала построй общий тренд, затем тот же показатель по платформе или каналу. Не начинай с десяти сегментов: они быстро превращают исследование в перебор. Выбери один разрез по гипотезе и один контрольный разрез, который может опровергнуть объяснение.
Для каждой линии запиши размер базы, период и правило активного пользователя. Если сегмент отличается, проверь, не изменился ли его состав. Затем сравни график с таблицей контрольных чисел и сформулируй следующий вопрос. Хорошая визуализация заканчивается не «красиво растёт», а конкретным действием: проверить событие, запустить эксперимент или не менять продукт.
В финальном отчёте оставь исходный DataFrame и код построения. Тогда любой коллега сможет поменять период, увидеть фильтр и проверить, что визуальный вывод не появился из ручного форматирования.
- Назвать вопрос до выбора типа графика.
- Собрать данные на смысловом зерне.
- Показать базу, единицы и неполные периоды.
- Отделить сигнал от сглаживания.
- Связать визуальный сигнал с проверяемым действием.
Материалы по теме

Pandas apply и векторизация: как писать быстрее и понятнее
Когда использовать apply, почему векторные операции быстрее и как переписать медленный построчный расчёт в pandas.

NumPy для аналитика: массивы, маски и векторные расчёты
Что нужно знать аналитику о NumPy: массивы, типы, boolean-маски, np.where и векторизация, которая лежит под многими операциями pandas.

Python для аналитика: рабочий стек из Jupyter, pandas и графиков
Какие инструменты нужны аналитику в Python: Jupyter, pandas, NumPy и matplotlib, как они связаны и с какого рабочего сценария начать.