Кладовочка

О разном, чтобы было под рукой онлайн

Шпаргалка по Matplotlib: графики от простых до публикационных

Matplotlib — основа визуализации в Python. Два API: pyplot (быстрый, для скриптов) и OO-API (объектный, для контроля). Шпаргалка покрывает оба — от первой линии до публикационных графиков.

Два API: pyplot vs OO

Matplotlib предоставляет два способа рисовать графики. Pyplot (plt.plot()) — быстрый для скриптов и ноутбуков, неявно хранит состояние. OO-API (fig, ax = plt.subplots()) — явные объекты, полный контроль над каждой деталью. Для всего, кроме быстрой разведки данных, используйте OO-API — он предсказуем и не ломается при усложнении.

import matplotlib.pyplot as plt
import numpy as np

# Pyplot API — неявное состояние (быстро, но меньше контроля)
plt.plot([1, 2, 3], [1, 4, 9])
plt.title('График')
plt.show()

# OO-API — явные объекты (рекомендуется для всего кроме разведки)
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot([1, 2, 3], [1, 4, 9])
ax.set_title('График')
fig.savefig('plot.png', dpi=150)
plt.close()  # освободить память

Основные типы графиков

Полный набор стандартных визуализаций: линии для трендов, scatter для корреляций, bar/barh для категорий, hist для распределений, pie для долей, boxplot для статистик, fill_between для диапазонов, imshow для матриц. Каждый тип решает свою задачу.

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# Линейный график
fig, ax = plt.subplots()
ax.plot(x, y1, label='sin', color='blue', linewidth=2, linestyle='--')
ax.plot(x, y2, label='cos', color='red', linewidth=1.5)
ax.legend(); ax.grid(True, alpha=0.3)
ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_title('Синус и косинус')

# Точечный график (scatter)
fig, ax = plt.subplots()
colors = np.random.rand(50)
sizes = np.random.rand(50) * 200
ax.scatter(x[:50], y1[:50], c=colors, s=sizes, alpha=0.6,
           cmap='viridis', edgecolors='black', linewidth=0.5)
plt.colorbar(ax.collections[0], ax=ax)

# Столбчатая диаграмма
categories = ['A', 'B', 'C', 'D']
values = [23, 45, 56, 78]
errors = [3, 5, 2, 4]
ax.bar(categories, values, yerr=errors, capsize=5,
       color=['#2196F3', '#4CAF50', '#FF9800', '#F44336'],
       edgecolor='black', linewidth=0.5)
# Добавить значения над столбцами
for bar, val in zip(ax.patches, values):
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
            str(val), ha='center', va='bottom')

# Горизонтальный bar
ax.barh(categories, values, color='steelblue')

# Гистограмма
data = np.random.randn(1000)
ax.hist(data, bins=30, density=True, alpha=0.7, edgecolor='black')

# Несколько распределений на одном графике
ax.hist(np.random.randn(1000), bins=30, alpha=0.5, label='Группа A')
ax.hist(np.random.randn(1000)+2, bins=30, alpha=0.5, label='Группа B')
ax.legend()

# Круговая диаграмма
sizes = [30, 25, 20, 15, 10]
labels = ['A', 'B', 'C', 'D', 'E']
explode = (0, 0.1, 0, 0, 0)  # выдвинуть сектор B
ax.pie(sizes, labels=labels, explode=explode, autopct='%1.1f%%',
       startangle=90, shadow=True)
ax.axis('equal')  # круг, не эллипс

# Boxplot
data = [np.random.randn(100), np.random.randn(100)+1, np.random.randn(100)-1]
ax.boxplot(data, labels=['A', 'B', 'C'], patch_artist=True)

# Заливка между линиями (fill_between)
ax.fill_between(x, y1 - 0.1, y1 + 0.1, alpha=0.3, label='±0.1')

# График с ошибками (errorbar)
ax.errorbar(x[:10], y1[:10], yerr=0.1, fmt='o', capsize=3, capthick=1)

# Heatmap / imshow
matrix = np.random.rand(10, 10)
ax.imshow(matrix, cmap='viridis', aspect='auto')
plt.colorbar(ax.images[0], ax=ax)

Subplots и компоновка

Несколько графиков на одной фигуре. subplots — простая сетка N×M. GridSpec — гибкая: ячейки разного размера, spanning. subplot_mosaic — самая удобная (именованные ячейки через ASCII-схему). tight_layout() исправляет наложение подписей.

# Простая сетка
fig, axes = plt.subplots(2, 3, figsize=(15, 8))
axes = axes.flatten()  # в плоский массив для итерации

for i, ax in enumerate(axes):
    ax.plot(x, np.sin(x + i))
    ax.set_title(f'Смещение {i}')

plt.tight_layout()  # исправить наложение

# GridSpec — гибкая сетка
from matplotlib.gridspec import GridSpec

fig = plt.figure(figsize=(12, 8))
gs = GridSpec(3, 3, figure=fig)

ax1 = fig.add_subplot(gs[0, :])        # весь верхний ряд
ax2 = fig.add_subplot(gs[1, 0:2])      # левая часть среднего ряда
ax3 = fig.add_subplot(gs[1, 2])        # правый столбец среднего
ax4 = fig.add_subplot(gs[2, 0])        # левый нижний
ax5 = fig.add_subplot(gs[2, 1:])       # правые два нижних

# subplot_mosaic — ещё удобнее (Python 3.7+)
fig, axes = plt.subplot_mosaic([
    ['A', 'A', 'A'],
    ['A', 'A', 'A'],
    ['B', 'C', 'C'],
    ['B', 'D', 'E'],
], figsize=(12, 10))

axes['A'].plot(x, y1); axes['A'].set_title('Главный график')
axes['B'].bar(categories, values); axes['B'].set_title('Bar')
axes['C'].scatter(x, y2); axes['C'].set_title('Scatter')
axes['D'].hist(data); axes['D'].set_title('Hist')
axes['E'].pie(sizes); axes['E'].set_title('Pie')

Оформление

Внешний вид графика: размер, фон, сетка, легенда, подписи осей, тики, рамки. Хорошее оформление отличает «черновой» график от публикационного. Настройка через ax.set_* и ax.spines.

# Размер и фон
fig, ax = plt.subplots(figsize=(12, 6))
fig.patch.set_facecolor('white')         # фон фигуры
ax.set_facecolor('#f5f5f5')             # фон области графика

# Сетка
ax.grid(True, alpha=0.3, linestyle='--', linewidth=0.5)
ax.grid(axis='y', which='major', color='gray', alpha=0.2)

# Легенда
ax.legend(loc='upper right', frameon=True, fancybox=True, shadow=True)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')  # за пределами

# Подписи осей
ax.set_xlabel('Время (сек)', fontsize=12, fontweight='bold')
ax.set_ylabel('Значение', fontsize=12)
ax.set_title('Заголовок', fontsize=14, fontweight='bold', pad=15)

# Пределы и масштаб
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)
ax.set_xscale('log')     # логарифмическая шкала
ax.set_yscale('symlog')  # симметричная логарифмическая
ax.set_aspect('equal')   # одинаковый масштаб по осям

# Тики (деления)
ax.set_xticks([0, np.pi, 2*np.pi])
ax.set_xticklabels(['0', 'π', '2π'], fontsize=10)
ax.tick_params(axis='both', which='major', labelsize=10, rotation=45)

# Настройка тиков через locator/formatter
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
ax.xaxis.set_major_locator(MultipleLocator(0.5))
ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))

# Спрятать рамки (классический стиль)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

Двойные оси (twin axes)

Два графика с разными шкалами Y на одном поле. Например: температура (левая ось, °C) и влажность (правая ось, %). twinx() создаёт вторую ось Y с общим X. Легенду нужно собирать вручную из обеих осей.

fig, ax1 = plt.subplots(figsize=(10, 6))

# Первая ось (левая)
ax1.plot(x, y1, 'b-', label='Температура')
ax1.set_xlabel('Время')
ax1.set_ylabel('Температура (°C)', color='blue')
ax1.tick_params(axis='y', labelcolor='blue')

# Вторая ось (правая)
ax2 = ax1.twinx()
ax2.plot(x, y2*100, 'r-', label='Влажность')
ax2.set_ylabel('Влажность (%)', color='red')
ax2.tick_params(axis='y', labelcolor='red')

# Объединённая легенда
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')

Стили и темы

Готовые наборы оформления: ggplot (как в R), seaborn-v0_8 (чистый современный), fivethirtyeight (блоговый), dark_background. Можно комбинировать, временно применять (with plt.style.context) и настраивать глобально через rcParams.

# Встроенные стили
print(plt.style.available)  # ['ggplot', 'seaborn-v0_8', 'fivethirtyeight', ...]

plt.style.use('ggplot')
plt.style.use('seaborn-v0_8-darkgrid')
plt.style.use('fivethirtyeight')

# Временный стиль (контекстный менеджер)
with plt.style.context('dark_background'):
    fig, ax = plt.subplots()
    ax.plot(x, y1)

# Комбинация стилей
plt.style.use(['seaborn-v0_8', 'seaborn-v0_8-pastel'])

# Кастомные настройки через rcParams
plt.rcParams.update({
    'figure.figsize': (10, 6),
    'font.size': 12,
    'axes.titlesize': 14,
    'axes.labelsize': 12,
    'xtick.labelsize': 10,
    'ytick.labelsize': 10,
    'legend.fontsize': 10,
    'lines.linewidth': 2,
    'figure.dpi': 100,
    'savefig.dpi': 150,
    'savefig.bbox': 'tight',
})

# Временные rcParams
with plt.rc_context({'lines.linewidth': 3, 'font.size': 16}):
    fig, ax = plt.subplots()
    ax.plot(x, y1)

Colormaps (цветовые карты)

Цветовые схемы для визуализации третьего измерения: sequential (от светлого к тёмному), diverging (двухцветные, для отклонений), qualitative (разные цвета для категорий). Выбор правильной colormap критичен для восприятия данных.

# Типы colormaps:
# Sequential: 'viridis', 'plasma', 'inferno', 'magma', 'cividis'
# Diverging: 'RdBu', 'coolwarm', 'seismic', 'Spectral'
# Qualitative: 'tab10', 'Set1', 'Pastel1', 'Dark2'
# Cyclic: 'twilight', 'hsv'

# Применение
sc = ax.scatter(x, y, c=values, cmap='coolwarm')
plt.colorbar(sc, ax=ax, label='Значение')

# Реверс cmap
ax.scatter(x, y, c=values, cmap='coolwarm_r')  # суффикс _r

Аннотации и текст

Текст, стрелки, линии-указатели на графике. text() — произвольный текст в координатах данных или осей. annotate() — текст со стрелкой (идеально для «вот здесь пик»). axhline/axvline — горизонтальные/вертикальные линии-ориентиры.

# Текст в координатах данных
ax.text(5, 0.5, 'Максимум', fontsize=12, ha='center')

# Текст в координатах осей (0-1)
ax.text(0.95, 0.95, '(a)', transform=ax.transAxes,
        fontsize=14, fontweight='bold', va='top', ha='right')

# Аннотация со стрелкой
ax.annotate('Пик', xy=(np.pi/2, 1.0), xytext=(np.pi/2+2, 0.5),
            arrowprops=dict(arrowstyle='->', color='red', lw=1.5),
            fontsize=12, color='red')

# Горизонтальные/вертикальные линии
ax.axhline(y=0, color='black', linewidth=0.5)       # y=0
ax.axvline(x=np.pi, color='red', linestyle='--')    # x=π
ax.axhspan(-0.5, 0.5, alpha=0.2, color='blue')      # заливка полосы

3D-графики

Трёхмерные визуализации через mpl_toolkits.mplot3d: поверхности (plot_surface), контуры (contour), scatter и линии в 3D. Требуют projection='3d' при создании осей. Хороши для визуализации функций двух переменных.

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

# Поверхность
X = np.linspace(-5, 5, 50)
Y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(X, Y)
Z = np.sin(np.sqrt(X**2 + Y**2))

surf = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
fig.colorbar(surf, ax=ax, shrink=0.5, aspect=10)

# Линии уровня (контуры)
ax.contour(X, Y, Z, zdir='z', offset=-1, cmap='viridis', alpha=0.5)

ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('Z')
ax.set_title('3D поверхность')

Сохранение

Экспорт графика в файл: PNG (растр, для веба/презентаций), PDF/SVG (вектор, для статей/печати). Ключевые параметры: dpi (качество растра), bbox_inches='tight' (обрезать поля), transparent=True (прозрачный фон). Для многостраничных отчётов — PdfPages.

# PNG (растровый) — для веба, презентаций
fig.savefig('plot.png', dpi=150, bbox_inches='tight')

# PNG с прозрачным фоном
fig.savefig('plot.png', dpi=150, bbox_inches='tight', transparent=True)

# PDF (векторный) — для статей, печати
fig.savefig('plot.pdf', bbox_inches='tight')

# SVG (векторный) — для веба, редактирования
fig.savefig('plot.svg', bbox_inches='tight')

# Многостраничный PDF
from matplotlib.backends.backend_pdf import PdfPages
with PdfPages('report.pdf') as pdf:
    for i in range(5):
        fig, ax = plt.subplots()
        ax.plot(x, np.sin(x + i))
        pdf.savefig(fig)
        plt.close(fig)

# Высокое качество для публикаций
fig.savefig('plot.png', dpi=300, bbox_inches='tight', pad_inches=0.1)

Анимация

Анимированные графики через FuncAnimation: функция animate(i) вызывается для каждого кадра, возвращая обновлённые данные. Сохранение в GIF (pillow writer) или MP4 (ffmpeg). blit=True ускоряет отрисовку, перерисовывая только изменившиеся элементы.

from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 100)
line, = ax.plot([], [], lw=2)
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)

def init():
    line.set_data([], [])
    return line,

def animate(i):
    line.set_data(x, np.sin(x + i*0.1))
    return line,

anim = FuncAnimation(fig, animate, init_func=init,
                     frames=200, interval=50, blit=True)

# Сохранить как GIF
anim.save('animation.gif', writer='pillow', fps=30)

# Сохранить как MP4 (нужен ffmpeg)
# anim.save('animation.mp4', writer='ffmpeg', fps=30)

Интерактивность

Реакция на действия пользователя: клики мыши (button_press_event), нажатия клавиш (key_press_event). Через fig.canvas.mpl_connect() можно добавить свои обработчики — например, ставить точки по клику или переключать данные по клавише.

# Обработчик кликов
def on_click(event):
    if event.inaxes:  # клик внутри области графика
        ax.plot(event.xdata, event.ydata, 'ro')
        fig.canvas.draw()

fig.canvas.mpl_connect('button_press_event', on_click)

# Обработчик клавиш
def on_key(event):
    if event.key == 'r':
        ax.clear()
        ax.plot(x, y1)
        fig.canvas.draw()

fig.canvas.mpl_connect('key_press_event', on_key)

Производительность

Для больших данных (>10k точек) стандартный scatter тормозит. Решения: plot(..., 'o') вместо scatter, растеризация (rasterized=True), blitting для анимаций. Выбор правильного подхода важен, когда график строится не в ноутбуке, а в production-пайплайне.

# Для больших данных (>10k точек)
# Использовать plot вместо scatter
ax.plot(x_big, y_big, 'o', markersize=1)  # быстрее чем scatter

# Растеризация scatter (векторная сетка + растровые точки)
ax.scatter(x_big, y_big, s=1, rasterized=True)

# Blitting для анимаций
anim = FuncAnimation(fig, animate, init_func=init,
                     frames=200, interval=50, blit=True)  # быстрее

Публикационный чек-лист

Готовый шаблон для создания графика, который не стыдно показать: информативный заголовок, подписанные оси, сетка, легенда, чистый фон без лишних рамок, высокое разрешение при сохранении. Пройдитесь по пунктам — получите публикационное качество.

fig, ax = plt.subplots(figsize=(10, 6))

# 1. Данные
ax.plot(x, y1, label='Ваш график', color='#2196F3', linewidth=2)

# 2. Заголовок и подписи
ax.set_title('Информативный заголовок', fontsize=14, fontweight='bold')
ax.set_xlabel('Ось X (единицы)', fontsize=12)
ax.set_ylabel('Ось Y (единицы)', fontsize=12)

# 3. Сетка
ax.grid(True, alpha=0.3, linestyle='--')

# 4. Легенда
ax.legend(loc='upper right', frameon=True, fontsize=11)

# 5. Оформление
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
fig.patch.set_facecolor('white')

# 6. Сохранить
fig.savefig('publication_plot.png', dpi=300, bbox_inches='tight')

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

Ошибка Правильно
plt.show() в цикле без plt.close() Утечка памяти! Закрывай фигуры
plt.show() очищает фигуру Сохраняй ДО show()
Забыт plt.tight_layout() Подписи обрезаются
ax.set_xlim() после ax.plot() OK, но до savefig()
Не тот transform= в аннотации ax.transData / ax.transAxes
pyplot в функциях/циклах Используй OO-API
Китайские/русские символы — квадраты Настрой шрифт: plt.rcParams['font.family'] = 'DejaVu Sans'

Ресурсы

Что дальше


Связанные шпаргалки: Python, NumPy, Pandas, Seaborn, SQL для начинающих