Графики в Python чаще всего строят библиотекой matplotlib: ставим её командой pip install matplotlib, создаём фигуру и оси через fig, ax = plt.subplots(), рисуем ax.plot(x, y) и выводим окно plt.show(). Все примеры ниже проверены на Python 3.14.3 с matplotlib 3.11.2 и NumPy 2.5.3, каждый запускается как отдельный скрипт. Импортируем модуль pyplot под общепринятым коротким именем plt, NumPy под именем np: так их называют почти в любом коде и в документации.
- Установка matplotlib
- Figure и Axes: из чего состоит график в matplotlib
- Основные типы графиков в matplotlib
- Линейный график: plot, подписи осей, легенда
- Точечный график: scatter
- Столбчатая диаграмма: bar
- Гистограмма: hist
- Круговая диаграмма: pie
- Несколько графиков на одной фигуре: subplots
- Как сохранить график в файл: savefig
- Предупреждения и ошибки matplotlib
- Частые вопросы
- Нужен ли plt.show() в Jupyter Notebook?
- Как построить 3D-график?
- Как построить график функции, которую пользователь ввёл строкой?
- Чем matplotlib отличается от seaborn и Plotly?
Установка matplotlib
Библиотека matplotlib ставится из PyPI вместе с зависимостями, среди них NumPy и Pillow, отдельно их ставить не нужно:
pip install matplotlib
Если в системе несколько версий Python, надёжнее вызывать pip через нужный интерпретатор: python -m pip install matplotlib, а на Windows py -m pip install matplotlib. Проверить, что установка прошла:
import matplotlib
print(matplotlib.__version__)
3.11.2
В Jupyter Notebook ставят из ячейки командой %pip install matplotlib. В Google Colab matplotlib обычно уже установлена, импорт работает сразу. Для примеров ниже хватит списков, срезов и циклов; если со списками и строками пока трудно, сначала разберите задачу «Фруктовый салат» на Python.
Figure и Axes: из чего состоит график в matplotlib
Фигура (Figure) отвечает за всё окно или картинку целиком. Внутри неё лежат оси (Axes): прямоугольник, где рисуется график, со своими шкалами, подписями и заголовком. На одной фигуре могут быть одни оси или сетка из нескольких. Не путайте Axes с Axis: второе слово означает одну ось координат, x или y, с делениями и подписями.
У matplotlib два интерфейса. Неявный идёт через функции модуля pyplot: plt.plot(), plt.title(). Этот набор функций сделан по образцу MATLAB, и, как в MATLAB, pyplot сам помнит текущую фигуру и текущие оси. Явный, объектно-ориентированный подход: создаём объекты fig и ax с помощью plt.subplots(), затем вызываем их методы. Оба способа рисуют одно и то же. Пишите в объектном стиле всё, кроме быстрых проверок в консоли, и начинайте с fig, ax = plt.subplots() уже на первом графике: когда понадобится второй график рядом, достаточно поменять аргументы subplots и добавить индекс к ax, а вызовы методов останутся прежними.
Так выглядит простой график в стиле pyplot:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [1, 4, 9, 16], marker="o")
plt.title("Квадраты чисел")
plt.xlabel("n")
plt.ylabel("n²")
plt.show()
Те же действия в объектном стиле называются почти так же, но у части методов Axes есть приставка set_:
| Функция pyplot | Метод объекта |
|---|---|
plt.plot(x, y) | ax.plot(x, y) |
plt.title("...") | ax.set_title("...") |
plt.xlabel("...") | ax.set_xlabel("...") |
plt.ylabel("...") | ax.set_ylabel("...") |
plt.xlim(0, 10) | ax.set_xlim(0, 10) |
plt.legend() | ax.legend() |
plt.grid(True) | ax.grid(True) |
plt.savefig("a.png") | fig.savefig("a.png") |
Если написать ax.xlabel("x") по аналогии с pyplot, программа упадёт:
AttributeError: 'Axes' object has no attribute 'xlabel'. Did you mean: 'clabel'?
Подсказка интерпретатора про clabel здесь мимо, нужен ax.set_xlabel.
Основные типы графиков в matplotlib
Для визуализации данных в большинстве задач хватает пяти методов Axes. Какой использовать, зависит от вопроса к данным:
| Что нужно показать | Тип графика | Метод |
|---|---|---|
| Как величина меняется: функция, ряд по времени | линейный график | ax.plot() |
| Есть ли связь между двумя величинами | диаграмма рассеяния | ax.scatter() |
| Сравнение категорий | столбчатая диаграмма | ax.bar() |
| Распределение значений | гистограмма | ax.hist() |
| Доли целого | круговая диаграмма | ax.pie() |
Линейный график: plot, подписи осей, легенда
Построим синус и косинус на одном графике. Массив x готовим функцией np.linspace(0, 10, 200): 200 точек от 0 до 10, между которыми plot проводит прямые отрезки, поэтому кривая выглядит гладкой.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 200) # 200 точек от 0 до 10
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(x, np.sin(x), color="blue", linewidth=2, label="sin(x)")
ax.plot(x, np.cos(x), color="red", linestyle="--", label="cos(x)")
ax.set_title("Синус и косинус", fontsize=14)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.grid(True)
ax.legend()
plt.show()
На экране две волны: синяя сплошная линия толщиной 2 и красная пунктирная, в углу легенда с подписями. figsize=(8, 4) задаёт ширину и высоту фигуры в дюймах, fontsize=14 размер шрифта заголовка. Параметр color принимает имя цвета линии ("red"), код вида "#1f77b4" или цвет из стандартной палитры "tab:blue". Кириллица в подписях работает без настройки: шрифт по умолчанию DejaVu Sans содержит русские буквы.
Легенда берёт текст из параметра label. Если вызвать ax.legend(), а подписи линиям не дать, легенда не появится, а matplotlib выведет предупреждение:
UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.

Точечный график: scatter
Диаграмма рассеяния показывает пары значений точками и нужна, чтобы увидеть связь между двумя величинами. Данные сгенерируем: рост и вес ста человек со случайным разбросом.
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
height = rng.normal(170, 8, 100) # рост, см
weight = height * 0.9 - 85 + rng.normal(0, 6, 100) # вес, кг
fig, ax = plt.subplots()
ax.scatter(height, weight, s=20, alpha=0.6, color="green")
ax.set_xlabel("Рост, см")
ax.set_ylabel("Вес, кг")
plt.show()
Облако из 100 зелёных точек вытянуто снизу слева вверх направо: чем выше рост, тем больше вес. Параметр s задаёт размер маркера, alpha=0.6 делает точки полупрозрачными, и там, где они перекрываются, цвет гуще. С фиксированным default_rng(42) случайные числа при каждом запуске одинаковые, картинка не меняется.

Столбчатая диаграмма: bar
Столбчатая диаграмма удобна для сравнения категорий, например продаж по месяцам:
import matplotlib.pyplot as plt
months = ["Янв", "Фев", "Мар", "Апр", "Май", "Июн"]
sales = [120, 95, 140, 160, 150, 185]
fig, ax = plt.subplots()
bars = ax.bar(months, sales, color="tab:blue")
ax.bar_label(bars) # подписи значений над столбцами
ax.set_title("Продажи по месяцам")
ax.set_ylabel("Штук")
plt.show()
Получим шесть синих столбцов, над каждым число из списка sales, самый низкий в феврале (95), самый высокий в июне (185). Для горизонтальных столбцов есть ax.barh() с теми же аргументами: так проще читать длинные названия категорий.

Гистограмма: hist
Гистограмму путают со столбчатой диаграммой, но она показывает распределение и считает сама: делит диапазон значений на интервалы (bins) и рисует, сколько значений попало в каждый. Без параметра bins количество интервалов равно 10.
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(0)
data = rng.normal(loc=50, scale=10, size=1000)
fig, ax = plt.subplots()
counts, edges, patches = ax.hist(data, bins=20, edgecolor="white")
print(len(counts), counts.sum())
ax.set_xlabel("Значение")
ax.set_ylabel("Сколько раз встретилось")
plt.show()
20 1000.0
Метод возвращает высоты столбцов, границы интервалов и сами столбцы. Вывод подтверждает: интервалов 20, а сумма высот равна 1000, то есть каждое значение попало ровно в один столбец. На графике колокол с вершиной около 50.

Круговая диаграмма: pie
Круговая диаграмма показывает доли целого. Параметр autopct подписывает проценты внутри секторов, формат "%1.1f%%" даёт один знак после запятой:
import matplotlib.pyplot as plt
labels = ["Python", "JavaScript", "Java", "Другие"]
shares = [45, 30, 15, 10]
fig, ax = plt.subplots()
wedges, texts, autotexts = ax.pie(shares, labels=labels, autopct="%1.1f%%", startangle=90)
print([t.get_text() for t in autotexts])
ax.set_title("Языки в проекте")
plt.show()
['45.0%', '30.0%', '15.0%', '10.0%']
ax.pie() возвращает секторы, подписи и тексты процентов, по последним видно, что напечатано внутри секторов. startangle=90 поворачивает начало первого сектора на 90 градусов, то есть наверх, а дальше секторы идут против часовой стрелки: так по умолчанию, за это отвечает параметр counterclock=True. Больше пяти-шести секторов мы бы на круг не ставили: тонкие доли сливаются, и столбчатая диаграмма читается лучше.

Несколько графиков на одной фигуре: subplots
plt.subplots(nrows, ncols) создаёт сетку графиков на одной фигуре. По умолчанию nrows=1 и ncols=1, поэтому без аргументов получаются одни оси. Сетка 2 на 2 с графиками четырёх типов:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(8, 6), layout="constrained")
print(ax.shape)
ax[0, 0].plot(x, np.sin(x))
ax[0, 0].set_title("plot")
ax[0, 1].scatter(x, np.cos(x), s=5)
ax[0, 1].set_title("scatter")
ax[1, 0].bar(["A", "B", "C"], [3, 7, 5])
ax[1, 0].set_title("bar")
ax[1, 1].hist(np.random.default_rng(1).normal(size=500))
ax[1, 1].set_title("hist")
for a in ax.flat:
a.grid(True)
plt.show()
(2, 2)
ax здесь уже не одни оси, а двумерный массив NumPy: к каждому графику обращаются по строке и столбцу. layout="constrained" раздвигает графики, чтобы заголовки и подписи осей не перекрывали соседей; похожую работу делает fig.tight_layout(), его вызывают перед plt.show(). Цикл for a in ax.flat: в конце перебирает все оси и включает сетку на каждом графике, подробнее в статье про циклы while и for в Python.
Форма массива зависит от сетки. При одной строке, plt.subplots(1, 3), массив одномерный, и привычный ax[0, 1] падает:
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed
Нужно писать ax[1]. Если код должен работать с любой сеткой, передайте squeeze=False: тогда массив всегда двумерный, даже при subplots(1, 1).

Как сохранить график в файл: savefig
fig.savefig() берёт формат из расширения имени файла: .png, .jpg, .pdf, .svg.
import matplotlib.pyplot as plt
fig, ax = plt.subplots() # размер по умолчанию 6.4 x 4.8 дюйма
ax.plot([1, 2, 3], [2, 1, 3])
print(fig.get_size_inches(), fig.dpi)
fig.savefig("chart.png") # 640 x 480 пикселей
fig.savefig("chart_300.png", dpi=300, bbox_inches="tight")
fig.savefig("chart.svg") # векторный формат
[6.4 4.8] 100.0
Размер в пикселях равен размеру в дюймах, умноженному на dpi. По умолчанию это 6.4 x 100 = 640 на 4.8 x 100 = 480. С dpi=300 выходит 1920 x 1440, а bbox_inches="tight" ещё и обрезает пустые поля: в нашем прогоне chart_300.png получился 1671 x 1239. Для печати и вёрстки берите PDF, для веба SVG: векторный файл не теряет чёткость при любом масштабе. На SVG из одних линий dpi не влияет: файлы с dpi=100 и dpi=300 у нас вышли одного размера, оба 460.8 x 345.6 pt и по 16 548 байт.
Сохраняйте до plt.show(). В обычном скрипте show() останавливает программу, пока окно открыто, а после закрытия окна pyplot забывает текущую фигуру. Тогда plt.savefig() создаёт новую пустую фигуру и сохраняет её: на Windows с оконным бэкендом TkAgg у нас получился PNG 640 x 480, в котором все пиксели белые. fig.savefig() в том же опыте сохранил график нормально, потому что обращается к объекту фигуры напрямую.

Предупреждения и ошибки matplotlib
На сервере или в контейнере без экрана matplotlib может работать через неинтерактивный бэкенд Agg (модуль отрисовки без окон). Тогда plt.show() ничего не покажет и предупредит:
UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
В этом случае окно не нужно: выберите Agg явно и сохраняйте график в файл. Надёжнее вызывать matplotlib.use() до импорта pyplot; в 3.11.2 переключение сработало и после импорта, пока ни одно окно не открыто, но на это лучше не рассчитывать.
import matplotlib
matplotlib.use("Agg") # до импорта pyplot, для надёжности
import matplotlib.pyplot as plt
for i in range(25):
fig, ax = plt.subplots()
ax.plot([0, i], [0, i])
fig.savefig(f"agg_{i}.png")
plt.close(fig)
print(plt.get_fignums())
[]
Скрипт сохранил 25 файлов без единого предупреждения, а пустой список в выводе показывает, что открытых фигур не осталось.
Фигуры, созданные через pyplot, живут в памяти, пока их не закрыть. Если строить графики в цикле, то, когда открытых фигур станет больше 20, появится предупреждение:
RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory.
Лечится вызовом plt.close(fig) после fig.savefig() в каждой итерации, как в цикле выше.
Частые вопросы
Нужен ли plt.show() в Jupyter Notebook?
Нет. Jupyter автоматически показывает под ячейкой все фигуры, созданные в ней, поэтому plt.show() там можно не писать. В обычном скрипте без него окно не откроется.
Как построить 3D-график?
Трёхмерные графики рисуются теми же осями, нужно передать projection="3d" при их создании:
import matplotlib.pyplot as plt
import numpy as np
x = y = np.linspace(-3, 3, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.plot_surface(X, Y, Z, cmap="viridis")
plt.show()
Получится поверхность: кольцевая волна вокруг центра, раскрашенная по высоте от тёмно-фиолетового к жёлтому (палитра viridis).
Как построить график функции, которую пользователь ввёл строкой?
Строку сначала нужно разобрать в выражение, eval для чужого ввода не годится. Как это сделать безопасно, мы разбирали в статье про парсер математических выражений. Когда парсер вернул обычную функцию Python, остаётся посчитать её на массиве точек:
import matplotlib.pyplot as plt
import numpy as np
def f(x): # функция, которую вернул парсер
return x**2 - 3 * x
x = np.linspace(-5, 5, 200)
fig, ax = plt.subplots()
ax.plot(x, f(x))
plt.show()
NumPy считает f(x) сразу для всех 200 точек, цикл не нужен. На графике парабола с минимумом в точке x = 1.5.
Чем matplotlib отличается от seaborn и Plotly?
Seaborn построена поверх matplotlib и даёт короткие функции для статистических графиков. Метод plot() у таблиц pandas (Series и DataFrame) тоже рисует через matplotlib, это обёртка над plt.plot(). Plotly рисует интерактивные графики для браузера. Начинать стоит с matplotlib: подписи, размер и сохранение у графика seaborn всё равно настраиваются через объекты matplotlib.








