Прямой ответ
Разработчику matplotlib нужен не для «красивых отчётов», а как диагностический инструмент: распределение задержек, динамика метрики, форма данных перед агрегацией — пять строк кода, и картина видна. Минимально достаточный набор: понять разницу figure/axes (и всегда работать через fig, ax = plt.subplots()), четыре типа графиков (line — динамика, bar — сравнение категорий, hist — распределение, scatter — связь двух величин), и привычка подписывать оси/заголовок (график без подписей единиц — половина информации). Остальное matplotlib-знание добирается по мере задач; для интерактивных дашбордов продукт-уровня есть другие инструменты (Grafana — в статье про мониторинг стека).
Анатомия: figure, axes и почему plt.plot() — ловушка
Объектная модель: Figure — холст, Axes — конкретный график с осями (на холсте их может быть несколько). Интерфейс plt.plot(...) (pyplot state-machine) рисует «на текущий axes» — на одном-единственном графике работает, в скрипте с двумя графиками или в функции начинает рисовать не туда. Дисциплина, которая окупается сразу: fig, ax = plt.subplots() (или subplots(2, 1) для сетки) и все вызовы через ax.plot/ax.set_...; функции рисования принимают ax параметром — тестируемо и переиспользуемо. В Jupyter — %matplotlib inline (или widget для зума; ядра и память ноутбука — в статье про Jupyter); в скриптах без экрана — matplotlib.use("Agg") до pyplot и сохранение в файл.
Четыре графика, которые решают большинство задач
Line (ax.plot) — динамика во времени: задержки запросов, потребление памяти, счётчики; несколько рядов — с легендой, время по оси X — датой (datetime pandas matplotlib понимает). Bar (ax.bar/barh) — сравнение категорий: размеры таблиц, количество ошибок по типам; сортировка столбцов по значению (не по алфавиту) делает вывод мгновенным. Hist (ax.hist) — распределение: латентности (часто лог-шкала по X честнее), размеры записей; гистограмма «до/после» оптимизации — лучший аргумент в код-ревью. Scatter (ax.scatter) — связь величин: размер ответа от времени обработки, поиск выбросов-корреляций. Связка с pandas прямая: результат groupby().agg() (практика агрегаций) рисуется df.plot(kind="bar", ax=ax) — pandas-обёртка передаёт axes и не мешает.
Читаемость: мелочи, которые решают
График читают за секунды, если: оси подписаны с единицами (ax.set_xlabel("время, мс") — «мс» против «с» меняет выводы); заголовок — вывод, а не тема («P95 задержки выросли после деплоя 14:00» вместо «Задержки»); шкалы честные (лог-шкала для величин в порядки — ax.set_yscale("log"); усечённая ось Y на барчарте — манипуляция, даже непреднамеренная); лишнего нет (сетка помогает — ax.grid(alpha=0.3), 3D и тени — нет); цвета различимы (дефолтная палитра matplotlib нормальна, радуга из двадцати цветов — нет; для многих рядов — прямые подписи у линий вместо легенды). Размер шрифта/холста — под место просмотра (figsize=(10, 5), dpi=150 для экспорта).
Экспорт и интеграция
Сохранение — fig.savefig("plot.png", dpi=150, bbox_inches="tight") (bbox_inches режет пустые поля; для документов — SVG/PDF вектором). В скриптах/CI графики генерируются без показа (Agg-бэкенд) и кладутся рядом с артефактами прогона — «ночной отчёт с картинкой» полезнее цифр в логе. В веб-сервисе matplotlib — только для фоновой генерации (не в обработчике запроса: тяжеловесен и не потокобезопасен в интерактивных бэкендах); для живых дашбордов — Grafana/специализированные стеки. Для отчётов из ноутбуков — nbconvert в HTML/PDF с инлайн-графиками.
Когда matplotlib, а когда выше уровнем
Лестница выбора: быстрый диагностический график в ноутбуке/скрипте — matplotlib (или seaborn поверх него для статграфики); исследовательский анализ с интерактивом — plotly; продуктовые дашборды — Grafana/BI. Отдельная честная оговорка: matplotlib — низкоуровневый инструмент, «красивый отчётный стиль» в нём собирается руками (стили, rcParams); если задача — отчёты пачками, разумнее начать с шаблона-функции, которая принимает данные и возвращает готовый fig, а не копировать двадцать строк настройки в каждую ячейку.
Практический сценарий: разбор инцидента по логам
Картина, знакомая каждому дежурному: сервис начал отвечать медленно, в логах — тысячи строк с длительностями запросов. Читать их глазами бессмысленно; пять строк кода превращают лог в диагноз. Читаем JSON-логи в список, собираем временные ряды, рисуем два графика рядом (fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)): на верхнем — динамика медианы и 95-го перцентиля длительности по минутам (ax1.plot два ряда, легенда, подпись оси «мс»), на нижнем — гистограмма всех длительностей за проблемный час (ax2.hist(..., bins=100, log=True) — лог-шкала по оси Y, потому что быстрых запросов в тысячи раз больше медленных). Картина читается мгновенно: медиана почти не изменилась, а p95 пополз вверх в 14:03 — значит, деградирует хвост, а не «всё»; гистограмма показывает второй горб в районе трёх секунд — типичная подпись упершегося пула соединений или внешнего таймаута. Добавляем вертикальную линию ax1.axvline в момент деплоя — совпадение или нет, вопрос закрыт за две минуты вместо часа листания логов. Этот же график сохраняется в png и прикладывается к разбору инцидента — аргумент весомее любых слов.
FAQ
Почему в Jupyter график «дублируется» текстом <Axes...>?
Последнее выражение ячейки печатается: ставьте ; в конце или plt.show() явно — мелочь, которая путает новичков.
Как рисовать из функции чисто?
Функция принимает данные и ax=None, внутри if ax is None: fig, ax = plt.subplots(), возвращает ax — вызывающий решает, показывать, сохранять или вставлять в сетку.
Кириллица в подписях не работает?
В подписях matplotlib кириллица работает, если шрифт её содержит (DejaVu Sans — да); проблемы возникают с конкретными шрифтами из rcParams — проверьте plt.rcParams["font.family"] и наличие глифов.
matplotlib или plotly для отчёта руководителю? Статический PNG в почту/документ — matplotlib достаточно и быстрее делается. Если отчёт будут «крутить» (зум, наведение) — plotly в HTML. Для постоянных метрик — дашборд, а не файлы.
Источники: официальная документация matplotlib (usage tutorial, axes API) — ссылки в шапке.