Два интерфейса: pyplot и объектный
pyplot хорош для быстрого наброска, объектный стиль — для всего серьёзного. Документация рекомендует второй.
«pyplot — это удобный фасад над объектами. Удобство превращается в ловушку, как только графиков становится больше одного».
У Matplotlib два способа рисовать. pyplot (стейтовый): plt.plot(...), plt.title(...) — библиотека сама хранит «текущую» фигуру и оси. Объектно-ориентированный (OO): вы явно получаете fig, ax и зовёте методы на них. Внешне результат одинаков, но управляемость разная.
# Стейтовый стиль (pyplot) — быстро, но "магия текущей оси"
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [4, 1, 7])
plt.title("Быстрый набросок")
plt.xlabel("x")
plt.show()
# Объектно-ориентированный стиль — явно и предсказуемо
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [4, 1, 7])
ax.set_title("Управляемый график")
ax.set_xlabel("x")
plt.show()
В OO-стиле каждый вызов адресован конкретному объекту: нет «текущей» оси, которую легко потерять. Поэтому для функций, переиспользуемого кода и многопанельных фигур используют именно его.
Разница становится очевидной, как только вы пишете функцию, которая рисует график. Хорошая практика — передавать в неё ax аргументом: def plot_sales(ax, data): ax.plot(...). Тогда вызывающий код сам решает, в какую панель встроить результат, и одну и ту же функцию можно использовать и для отдельной картинки, и для одной из ячеек сетки 3×3. С pyplot так не выйдет: функция, дёргающая plt.plot(), всегда рисует в «текущую» ось, и две такие функции подряд незаметно затопчут друг друга. Именно поэтому весь серьёзный код визуализации — в библиотеках, дашбордах, отчётных пайплайнах — написан в объектном стиле.
Стоит развеять и распространённое заблуждение, будто это «два разных движка». Движок один и тот же — Artist-дерево и backend. plt.subplots(), plt.figure(), plt.show() остаются удобными точками входа даже в строго объектном коде: ими создают Figure и запускают показ. Запрещать pyplot целиком не нужно — вредно лишь смешивать уровни внутри одного графика, когда часть команд идёт через ax, а часть через глобальную «текущую» ось. Практическое правило: plt — чтобы создать фигуру и показать её, ax и fig — для всего, что внутри.
pyplot объектный (OO)
plt.plot() ax.plot()
| |
ищет "текущую" Axes адресует ax напрямую
| |
удобно для 1 графика надёжно для N графиков
| |
скрытое состояние явные ссылки fig, ax
Как работает под капотом
pyplot хранит глобальный стек фигур и указатель на «текущую» Axes (plt.gca() — get current axes). Каждый plt.plot() внутри делает gca().plot(). То есть pyplot — это тонкая обёртка, которая за вас вызывает методы Axes. В OO вы делаете это сами и потому всегда знаете, куда попадёт команда.
Чтобы построить даже простую линию, нужно превратить функцию в пары (x, y). Сгенерируем их вручную.
# Готовим точки для линии y = x^2 - 3x + 2 без numpy
xs = [i * 0.5 for i in range(-2, 9)] # шаг 0.5
ys = [x * x - 3 * x + 2 for x in xs]
for x, y in zip(xs, ys):
print("x={:>5} y={:>6.2f}".format(x, y))
# минимум функции — там, где y наименьший
ymin = min(ys)
xmin = xs[ys.index(ymin)]
print("Минимум около x =", xmin, " y =", round(ymin, 2))
«Попробуй сам ▶» — это те самые координаты, что передаются в ax.plot(xs, ys). График — лишь визуализация этой таблицы.
Частые ошибки
Смешивать стили: создать fig, ax, а потом звать plt.xlabel() — подпись уйдёт на «текущую» ось, которая может оказаться не той. Полагаться на «текущую» ось в цикле по subplots. Забывать, что в Jupyter повторный запуск ячейки рисует поверх старой «текущей» фигуры.
Несколько ловушек связаны именно с переименованием методов между стилями, и они сбивают новичков. В pyplot вы пишете plt.xlabel(), plt.xlim(), plt.title(), а в объектном стиле те же действия называются иначе: ax.set_xlabel(), ax.set_xlim(), ax.set_title() — с приставкой set_. Попытка позвать ax.xlabel() завершится ошибкой «нет такого метода», и человек теряется. Обратная путаница тоже частая: plt.set_xlabel() не существует. Помогает простое правило: у plt — глаголы без приставки, у ax — почти всегда set_/get_. И отдельно: в цикле for ax in axes: легко по привычке вставить plt.plot() вместо ax.plot() — тогда все кривые лягут в одну «текущую» панель, а не разойдутся по сетке.
Best practices
- По умолчанию — объектный стиль:
fig, ax = plt.subplots(). - pyplot оставьте для одноразовых набросков в интерактиве.
- Никогда не смешивайте
plt.*иax.*в одном графике.
Итог: объектный стиль — ваша дисциплина на весь курс. Теперь научимся настраивать сам график: подписи, легенду, сетку и стиль.