Django-Admin.ru

Matplotlib: графики в Python

Библиотека Matplotlib в Python: установка, линейный график plot, bar, hist, scatter, subplots, подписи и кириллица, сохранение через savefig и backend Agg.

Редакция Django-Admin.ru
· обновлено · 9 мин чтения

Содержание · 13 разделов
  1. Установка Matplotlib
  2. pyplot и объектный стиль: fig, ax
  3. Линейный график: plot
  4. Столбчатая диаграмма: bar
  5. Гистограмма: hist
  6. Диаграмма рассеяния: scatter
  7. Несколько графиков: subplots
  8. Подписи и кириллица
  9. Сохранение: savefig
  10. Backend Agg: графики на сервере без экрана
  11. Частые ошибки
  12. Окно с графиком не появляется
  13. Сохранилась пустая картинка
  14. Подписи обрезаны или наезжают друг на друга
  15. Квадраты вместо русских букв
  16. Графики накладываются друг на друга
  17. Частые вопросы
  18. Как подписать даты на оси X?
  19. Как построить два графика с разными осями Y?
  20. Как сделать логарифмическую шкалу?
  21. Как отдать график в веб-приложение без сохранения на диск?
  22. Как анимировать график?
  23. Итог

Matplotlib — основная библиотека Python для построения графиков: линейных, столбчатых, гистограмм и диаграмм рассеяния. Она сохраняет картинки в PNG, SVG и PDF и показывает их в окне или в Jupyter. Минимальный пример matplotlib на Python:

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
plt.title("Квадраты чисел")
plt.savefig("quick.png")  # сохранить в файл
plt.show()                # открыть окно с графиком

Ниже — установка, два стиля работы с библиотекой, основные типы графиков, несколько графиков на одной картинке, подписи и кириллица, сохранение в файл и работа на сервере без экрана. Примеры проверены на Matplotlib 3.11 и Python 3.12.

Установка Matplotlib

Matplotlib ставится через pip, лучше в виртуальное окружение проекта. NumPy установится автоматически как зависимость:

# Windows
py -m pip install matplotlib

# macOS / Linux
python3 -m pip install matplotlib

Проверка:

import matplotlib

print(matplotlib.__version__)  # → 3.11.2 (у вас может быть другая версия)

Пакет называется matplotlib; команды вроде pip install matplotlib.pyplot или pip install pyplot не сработают — pyplot это модуль внутри пакета. Если импорт падает с ModuleNotFoundError: No module named 'matplotlib', библиотека установлена в другой интерпретатор: см. разбор ModuleNotFoundError и установку библиотек Python.

pyplot и объектный стиль: fig, ax

У Matplotlib два способа рисовать.

  1. Через plt (модуль pyplot) — функции plt.plot(), plt.title() работают с «текущим» графиком. Удобно для быстрых проверок.
  2. Объектный стиль — вы явно создаёте фигуру fig (вся картинка) и оси ax (одна область с графиком) и вызываете методы у них. Такой код проще читать и поддерживать, особенно когда графиков несколько.
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

fig, ax = plt.subplots(figsize=(8, 4))   # размер в дюймах
ax.plot(x, np.sin(x), label="sin(x)", color="tab:blue", linewidth=2)
ax.plot(x, np.cos(x), label="cos(x)", color="tab:orange", linestyle="--")
ax.set_title("Синус и косинус")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()      # легенда из параметров label
ax.grid(True)    # сетка
fig.savefig("sin_cos.png", dpi=150, bbox_inches="tight")

Сопоставление функций двух стилей:

pyplotОбъектный стиль
plt.title()ax.set_title()
plt.xlabel()ax.set_xlabel()
plt.ylabel()ax.set_ylabel()
plt.xlim()ax.set_xlim()
plt.legend()ax.legend()
plt.savefig()fig.savefig()

Массивы для осей удобно готовить с помощью NumPy: np.linspace(0, 10, 100) даёт 100 равномерных точек от 0 до 10.

Линейный график: plot

ax.plot(x, y) соединяет точки линией. Внешний вид задают параметры:

  • color — цвет: "red", "tab:green", "#1f77b4";
  • linestyle — стиль линии: "-", "--", ":", "-.";
  • linewidth — толщина;
  • marker — маркер точек: "o", "s", "^";
  • label — подпись для легенды.
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [10, 15, 13, 18], marker="o", color="tab:red", label="Выручка")
ax.legend()

Столбчатая диаграмма: bar

ax.bar() строит вертикальные столбцы, ax.barh() — горизонтальные. По оси X можно передать строки — Matplotlib сам расставит подписи:

months = ["Янв", "Фев", "Мар", "Апр"]
sales = [120, 95, 140, 160]

fig, ax = plt.subplots()
bars = ax.bar(months, sales, color="tab:green")
ax.bar_label(bars)              # значения над столбцами
ax.set_ylabel("Продажи, шт.")
fig.savefig("bar.png")

Гистограмма: hist

Гистограмма показывает распределение значений: диапазон делится на интервалы (bins), и для каждого считается число попавших в него значений.

rng = np.random.default_rng(0)
data = rng.normal(loc=170, scale=10, size=1000)  # 1000 значений роста

fig, ax = plt.subplots()
counts, edges, patches = ax.hist(data, bins=20, edgecolor="black")
ax.set_xlabel("Рост, см")
ax.set_ylabel("Количество")
fig.savefig("hist.png")

print(len(counts), counts.sum())  # → 20 1000.0

hist() возвращает количество значений в каждом интервале, границы интервалов и сами столбцы — их можно использовать в дальнейших расчётах.

Диаграмма рассеяния: scatter

ax.scatter() рисует точки без линий — так видна связь двух величин. Размер (s) и цвет (c) точек могут зависеть от данных:

rng = np.random.default_rng(0)
x = rng.random(50)
y = x * 2 + rng.normal(0, 0.2, 50)

fig, ax = plt.subplots()
sc = ax.scatter(x, y, s=60, c=y, cmap="viridis", alpha=0.7)
fig.colorbar(sc, ax=ax, label="y")   # шкала цветов
fig.savefig("scatter.png")

Для долей подойдёт круговая диаграмма ax.pie([40, 35, 25], labels=["A", "B", "C"], autopct="%1.0f%%").

Несколько графиков: subplots

plt.subplots(строки, столбцы) создаёт сетку осей. Для сетки 2×2 переменная axes — массив NumPy формы (2, 2), к осям обращаются по индексам:

fig, axes = plt.subplots(2, 2, figsize=(10, 8))

axes[0, 0].plot([1, 2, 3], [3, 1, 2])
axes[0, 0].set_title("plot")
axes[0, 1].bar(["A", "B"], [3, 5])
axes[0, 1].set_title("bar")
axes[1, 0].hist(data, bins=15)
axes[1, 0].set_title("hist")
axes[1, 1].scatter([1, 2, 3], [3, 1, 2])
axes[1, 1].set_title("scatter")

fig.suptitle("Четыре типа графиков")  # общий заголовок
fig.tight_layout()                     # убрать наложение подписей
fig.savefig("subplots.png")

Если сетка в одну строку, например plt.subplots(1, 3), то axes одномерный: axes[0], axes[1], axes[2]. Параметры sharex=True и sharey=True делают общие оси для всех графиков.

Подписи и кириллица

Заголовки и подписи осей задают методы set_title(), set_xlabel(), set_ylabel(), а легенду — параметр label плюс ax.legend(). Метод ax.text(x, y, "текст") добавляет надпись в точку с координатами данных.

Шрифт по умолчанию в Matplotlib — DejaVu Sans, в нём есть кириллица, поэтому русские подписи из примеров выше отображаются без настроек. Проблема появляется, если в настройках выбран шрифт без русских букв: вместо текста выводятся пустые квадраты, а в консоли — предупреждение:

UserWarning: Glyph 1055 (\N{CYRILLIC CAPITAL LETTER PE}) missing from font(s) cmsy10.

Решение — указать шрифт с кириллицей в начале скрипта:

import matplotlib.pyplot as plt

plt.rcParams["font.family"] = "DejaVu Sans"  # или "Arial" на Windows

Если шрифта нет в системе, Matplotlib напишет findfont: Font family 'Имя' not found. и подставит шрифт по умолчанию.

Сохранение: savefig

fig.savefig() определяет формат по расширению файла:

fig.savefig("chart.png", dpi=150, bbox_inches="tight")  # растровая картинка
fig.savefig("chart.svg")                                 # вектор для сайта
fig.savefig("chart.pdf")                                 # вектор для печати
  • dpi — разрешение (по умолчанию берётся из figure.dpi, это 100);
  • bbox_inches="tight" — обрезать лишние поля, чтобы подписи не срезались;
  • transparent=True — прозрачный фон.

Вызывайте savefig() до plt.show(): после закрытия окна фигура удаляется, и сохранится пустая картинка. Если в цикле строится много графиков, закрывайте каждую фигуру через plt.close(fig) — иначе они копятся в памяти.

Backend Agg: графики на сервере без экрана

Backend — это механизм, который отрисовывает график: в окне (TkAgg, QtAgg, MacOSX) или только в файл. На сервере, в Docker или в CI экрана нет, поэтому используйте неинтерактивный backend Agg — он рисует PNG в память и в файлы:

import matplotlib
matplotlib.use("Agg")          # до первого построения графика

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [2, 4, 1])
fig.savefig("report.png")
plt.close(fig)

Без кода backend можно выбрать переменной окружения: MPLBACKEND=Agg python script.py (Linux, macOS). С Agg вызов plt.show() ничего не открывает и выдаёт предупреждение:

UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown

Так же графики строят внутри веб-приложений, например в админке Django: картинка сохраняется в файл или буфер и отдаётся пользователю.

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

Окно с графиком не появляется

В обычном скрипте график показывается только после plt.show(). Если вызов есть, а окна нет — проверьте, не выбран ли backend Agg (через matplotlib.use() или MPLBACKEND). В Jupyter графики выводятся в ячейке автоматически.

Сохранилась пустая картинка

savefig() вызван после plt.show(). Поменяйте порядок или сохраняйте через fig.savefig(), держа ссылку на фигуру.

Подписи обрезаны или наезжают друг на друга

Вызовите fig.tight_layout() перед сохранением или передайте bbox_inches="tight" в savefig().

Квадраты вместо русских букв

Выбран шрифт без кириллицы — задайте plt.rcParams["font.family"] со шрифтом, в котором она есть (см. раздел выше).

Графики накладываются друг на друга

В стиле plt все вызовы рисуют на текущей фигуре. Начинайте каждый новый график с fig, ax = plt.subplots() и закрывайте старые через plt.close(fig).

Частые вопросы

Как подписать даты на оси X?

Передайте в plot() список дат (datetime или столбец дат pandas) — Matplotlib сам расставит деления. Формат подписей задаёт DateFormatter, а autofmt_xdate() наклоняет подписи, чтобы они не наезжали друг на друга:

from datetime import date, timedelta
import matplotlib.dates as mdates

days = [date(2026, 1, 1) + timedelta(days=i) for i in range(30)]
fig, ax = plt.subplots()
ax.plot(days, range(30))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d.%m"))  # 01.01, 05.01 …
fig.autofmt_xdate()

Если даты хранятся строками, сначала преобразуйте их в даты (pd.to_datetime()): строки Matplotlib рисует как категории в том порядке, в каком они пришли.

Как построить два графика с разными осями Y?

Метод twinx() создаёт вторую ось Y справа с общей осью X — так на одной картинке помещаются величины разного масштаба:

fig, ax1 = plt.subplots()
ax1.plot([1, 2, 3], [10, 20, 15], color="tab:blue")
ax1.set_ylabel("Выручка, тыс. руб.", color="tab:blue")

ax2 = ax1.twinx()
ax2.plot([1, 2, 3], [0.5, 0.7, 0.6], color="tab:red")
ax2.set_ylabel("Конверсия", color="tab:red")

Как сделать логарифмическую шкалу?

Вызовите ax.set_yscale("log") (или ax.set_xscale("log") для оси X). Это нужно, когда значения отличаются на порядки: на обычной шкале маленькие значения сливаются с нулём. Отрицательные значения и ноль на логарифмической шкале не отображаются.

Как отдать график в веб-приложение без сохранения на диск?

Сохраните фигуру в буфер в памяти и отдайте байты как ответ с типом image/png или встройте в HTML через base64:

import base64
import io

buf = io.BytesIO()
fig.savefig(buf, format="png")
plt.close(fig)

png_bytes = buf.getvalue()                       # для HTTP-ответа
encoded = base64.b64encode(png_bytes).decode()   # для <img src="data:image/png;base64,...">

На сервере используйте backend Agg, как в разделе выше.

Как анимировать график?

FuncAnimation вызывает функцию для каждого кадра и обновляет данные линии. Готовую анимацию можно сохранить в GIF через PillowWriter (Pillow ставится вместе с Matplotlib):

from matplotlib.animation import FuncAnimation, PillowWriter

x = np.linspace(0, 2 * np.pi, 200)
fig, ax = plt.subplots()
(line,) = ax.plot(x, np.sin(x))

def update(frame):
    line.set_ydata(np.sin(x + frame / 10))
    return (line,)

anim = FuncAnimation(fig, update, frames=60, interval=50)
anim.save("wave.gif", writer=PillowWriter(fps=20))

Для показа в окне вместо save() вызовите plt.show() и держите ссылку anim до конца работы — иначе объект удалится сборщиком мусора и анимация остановится.

Итог

Для большинства задач достаточно fig, ax = plt.subplots(), одного из методов plot, bar, hist или scatter, подписей осей и fig.savefig(); на сервере добавьте backend Agg. Данные для графиков обычно готовят в NumPy или pandas — у DataFrame есть метод plot(), который рисует через Matplotlib. Другие пакеты для данных и графики — в обзоре библиотек Python, а примеры всех типов графиков — в галерее Matplotlib.

  • #Python
  • #Matplotlib
  • #Графики
  • #Визуализация
  • #NumPy
↑ ↓ выборEnter открытьEsc закрыть