Графики для анализа и для статьи: Matplotlib, палитры, seaborn, Plotly и HoloViews
Вячеслав Федоров
Лекция 11
Разработка и применение ПО
в физических исследованиях
plt
От выбора вида графика до интерактивной страницы
01
Зачем график
02
Фигура и оси
03
Графики для статьи
04
Поля и палитры
05
Статистика
06
Интерактивность
Показывать данные так, чтобы график не вводил в заблуждение ни автора, ни читателя
Для собственного анализа график строится быстро и сразу просматривается, для статьи — оформляется по правилам журнала и сохраняется в векторном формате, для коллег — становится интерактивной страницей. Во всех трёх случаях вид графика, масштаб осей и палитра выбираются по смыслу данных.
| Библиотека | Что даёт | Результат |
|---|---|---|
| Matplotlib | фигура и оси, любой вид графика | PNG, PDF, SVG |
| seaborn | статистика из таблицы | поверх Matplotlib |
| pandas .plot | быстрый взгляд на столбцы | поверх Matplotlib |
| Plotly | подсказки, масштаб, 3D | HTML |
| Bokeh, Panel | страницы и панели мониторинга | HTML, сервер |
| HoloViews | описание данных вместо графика | бэкенды bokeh, matplotlib, plotly |
Для физика
Статья — Matplotlib, разбор сырых данных — Plotly или HoloViews, мониторинг установки — Panel.
Пояснение
Основа почти всех статичных графиков Python — Matplotlib: seaborn и pandas строят поверх него.
Квартет Энскомба, выбор вида графика, честное изображение данных
01
Зачем график
Фигура и оси
Графики для статьи
Поля и палитры
Статистика
Интерактивность
| Статистика | Все четыре набора |
|---|---|
| среднее x | 9,00 |
| среднее y | 7,50 |
| дисперсия x | 11,00 |
| дисперсия y | 4,12–4,13 |
| корреляция | 0,816–0,817 |
| прямая | y = 0,500 x + 3,00 |
Ф. Энскомб, 1973: четыре набора по 11 точек с одинаковыми средними, дисперсиями, корреляцией и прямой регрессии устроены совершенно по-разному.
Важно
Сводные статистики не заменяют взгляда на данные: график — первый шаг анализа, а не последний.
01
Четыре набора по 11 точек
02
Средние, дисперсии, корреляция и прямая регрессии
03
Четыре графика рассеяния в одной фигуре
Что наблюдается
Средние 9 и 7,5 совпадают, дисперсия y — 4,12–4,13, корреляция — 0,816–0,817; у всех прямая y = 0,5x + 3. График сохранён в anscombe.png.
root@lab — запись со стенда
Что наблюдается
I — линейная связь с шумом, II — гладкая нелинейная кривая, III — прямая с одним выбросом, IV — наклон задаёт единственная точка. Прямая регрессии у всех одна.
| Задача | Вид графика | Где искать |
|---|---|---|
| распределение величины | гистограмма, ящик с усами | hist, boxplot |
| зависимость y(x) | линия | plot |
| связь двух величин | точечная диаграмма | scatter |
| измерения с погрешностью | точки с отрезками | errorbar |
| поле на сетке | цветовая карта, изолинии | imshow, contour |
| много параметров | параллельные координаты | Plotly |
Пояснение
Каталог видов графиков по назначению — «Периодическая таблица методов визуализации» (visual-literacy.org).
Важно
Круговые и объёмные диаграммы сравнивать трудно: длину глаз оценивает точнее, чем угол и площадь.
01
Подписи осей
величина и единицы: «Ток пучка, мА»
02
Масштаб
линейный или логарифмический — по смыслу; у столбцов ось начинается с нуля
03
Сравнимые панели
общие шкалы: sharex, sharey
04
Палитра
по смыслу величины, без радуги jet
05
Минимум украшений
без объёма, теней и лишних рамок
Важно
Обрезанная ось у столбцовой диаграммы многократно преувеличивает разницу.
Для физика
Спектры и профили пучка с большим динамическим диапазоном — в логарифмическом масштабе.
Два интерфейса, устройство фигуры, виды графиков, сохранение в файл
02
Зачем график
Фигура и оси
Графики для статьи
Поля и палитры
Статистика
Интерактивность
pyplot: скрытая текущая фигура
import matplotlib.pyplot as plt
plt.plot(t, current)
plt.xlabel('Время, мкс')
plt.savefig('a.png')
Объекты: фигура и оси названы явно
fig, ax = plt.subplots(figsize=(7, 3))
ax.plot(t, current)
ax.set_xlabel('Время, мкс')
fig.savefig('a.png')
Пояснение
Явные fig и ax — в любом коде длиннее нескольких строк: видно, к каким осям относится вызов.
Figure — холст и файл
Axes — оси с координатами
Line2D: ax.plot
Legend: ax.legend
Text: ax.annotate
Patch: ax.bar
XAxis и YAxis: деления, подписи, пределы
fig.savefig — сохраняет весь холст
| Объект | Что это |
|---|---|
| Figure | холст: поля, заголовок, файл |
| Axes | область графика с координатами |
| Axis | шкала: пределы, деления, масштаб |
| Artist | всё нарисованное: линии, текст |
Пояснение
На одной фигуре несколько осей: plt.subplots(2, 1) возвращает фигуру и массив из двух осей.
ax.plot(t, current) # линия
ax.scatter(x, y, s=10) # точки
ax.errorbar(k, s, 0.02, fmt='o') # погрешности
ax.hist(age, bins=16) # гистограмма
ax.bar(labels, values) # столбцы
ax.fill_between(z, -a, a) # полоса
ax.imshow(img, cmap='viridis') # поле
ax.contour(X, Y, Z, levels=10) # изолинии
ax.set_xscale('log') # масштаб
Пояснение
Каждый метод возвращает созданный объект: линию, набор точек, изображение — для последующей настройки.
Для физика
Галерея matplotlib.org/stable/gallery: вид графика подбирается по рисунку, код берётся готовым.
| Формат | Устройство | Для чего |
|---|---|---|
| PNG | растр, плотность dpi | слайды, веб |
| SVG | вектор | веб, правка в редакторе |
| вектор, шрифты внутри | статья, LaTeX |
fig.tight_layout()
fig.savefig('waveform.png', dpi=300)
fig.savefig('waveform.pdf')
Важно
На машине без экрана Matplotlib выбирает бэкенд Agg: plt.show() ничего не покажет, графики сохраняются в файлы.
Пояснение
В Jupyter фигура показывается под ячейкой сама, savefig нужен только для файла.
01
Осциллограмма с шумом и сглаживание Савицкого—Голея
02
Сохранение в PNG 150 и 300 dpi, SVG и PDF
03
Размеры изображений в точках
04
Просмотр в браузере: http.server
Что наблюдается
PNG 150 dpi — 66 КиБ и 1200×480 точек, 300 dpi — 151 КиБ и 2400×960; SVG — 86 КиБ, PDF — 38 КиБ. http.server отдаёт файлы браузеру.
root@lab — запись со стенда
Что наблюдается
Максимум сглаженного сигнала — 4,16 мА при 3,51 мкс; надпись со стрелкой — annotate, легенда собрана из label.
Подписи с формулами, погрешности, компоновка, размер и стиль
03
Зачем график
Фигура и оси
Графики для статьи
Поля и палитры
Статистика
Интерактивность
ax.errorbar(k, meas, yerr=0.02, fmt='o',
capsize=3, label='экран')
ax.plot(k, model, color='crimson', label='модель')
ax.set_xlabel(r'Сила линзы $k$, м$^{-1}$')
ax.set_ylabel(r'Размер пучка $\sigma$, мм')
ax.legend()
Пояснение
Текст в знаках доллара набирается mathtext — подмножеством TeX без установки LaTeX.
Важно
Строка с формулой помечается префиксом r, иначе обратный слеш обработает Python.
Пояснение
Полный LaTeX — text.usetex=True: нужен TeX, а для кириллицы — преамбула с T2A и babel.
plt.rcParams.update({
'figure.figsize': (3.4, 2.4), # колонка
'font.size': 9,
'axes.grid': True})
fig, (up, down) = plt.subplots(
2, 1, sharex=True,
layout='constrained')
fig.align_ylabels()
01
Размер под колонку
около 8,6 см, 3,4 дюйма: шрифт в статье равен шрифту текста
02
Общие оси
sharex и sharey: подписи только у крайних
03
Один стиль
rcParams или файл стиля для всего набора
Для физика
Все рисунки статьи строит один скрипт: правка стиля обновляет весь набор.
01
25 измерений размера пучка при разной силе линзы
02
errorbar, модель и подписи с формулами
03
Сохранение в PNG и PDF
Что наблюдается
Минимум модели — 0,158 мм при k = 1,08 1/м, между точками скана; PDF занимает 17 КБ, PNG — 46 КБ.
root@lab — запись со стенда
Что наблюдается
Подписи с k, σ и степенью −1 набраны mathtext без LaTeX; точки с засечками — errorbar, линия — модель, сетка — rcParams.
imshow, pcolormesh и изолинии; последовательные, расходящиеся и качественные палитры
04
Зачем график
Фигура и оси
Графики для статьи
Поля и палитры
Статистика
Интерактивность
| Функция | Сетка | Пример |
|---|---|---|
| imshow | равномерная, ячейка — пиксель | изображение, матрица |
| pcolormesh | неравномерная, узлы | поле на неравномерной сетке |
| contour, contourf | изолинии | потенциал, уровни поля |
| quiver, streamplot | векторное поле | силовые линии |
im = ax.imshow(By, cmap='RdBu_r', vmin=-lim, vmax=lim,
origin='lower', extent=(x0, x1, y0, y1))
fig.colorbar(im, ax=ax, label='$B_y$, мТл')
Пояснение
extent задаёт физические границы: на осях метры вместо номеров пикселей.
| Тип | Палитры | Для чего |
|---|---|---|
| последовательная | viridis, magma, cividis | знакопостоянная величина |
| расходящаяся | RdBu_r, coolwarm | знакопеременная, светлый центр — ноль |
| качественная | tab10, Set2 | категории без порядка |
| радуга | jet, rainbow | не рекомендуются |
Важно
У расходящейся палитры границы vmin и vmax симметричны, иначе белый смещается с нуля.
Пояснение
viridis монотонна по светлоте: читается в чёрно-белой печати и при нарушении цветового зрения.
Для физика
Профиль пучка и спектр с большим динамическим диапазоном — norm=LogNorm().
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, cmap='viridis')
ax.plot(x, y, z) # траектория
go.Figure(go.Surface(x=x, y=y, z=Z)) # Plotly
Важно
Значения с трёхмерной поверхности читаются плохо: для статьи — карта с изолиниями, для разбора — вращаемая поверхность Plotly.
Для физика
Траектории частиц в пространстве — ax.plot(x, y, z) или go.Scatter3d.
01
Матрица отклика замкнутой орбиты 64×48
02
Три палитры: jet, viridis, RdBu_r с симметричными границами
03
Число палитр в Matplotlib
Что наблюдается
Значения от −11,8 до 11,9 мм/мрад; в Matplotlib 182 палитры вместе с обращёнными вариантами _r.
root@lab — запись со стенда
Что наблюдается
В jet светлее всего промежуточные значения около ±3, а не наибольшие; viridis монотонна по светлоте; RdBu_r с симметричными границами показывает знак: белый — ноль.
Быстрые графики pandas, seaborn: распределения, группы, доверительные интервалы
05
Зачем график
Фигура и оси
Графики для статьи
Поля и палитры
Статистика
Интерактивность
df['Age'].plot.hist(bins=16)
df.boxplot(column='Age', by='Pclass')
log['p'].resample('1min').mean().plot()
ax = df.plot.scatter(x='Age', y='Fare')
ax.set_yscale('log')
Пояснение
Методы plot таблиц и столбцов строят графики Matplotlib и возвращают оси: оформление дописывается их методами.
Для физика
Быстрый взгляд на журнал или таблицу измерений — одна строка; для статьи те же оси оформляются по правилам третьей части.
| Функция | Что показывает |
|---|---|
| histplot, kdeplot | распределение |
| boxplot, violinplot | распределение по группам |
| barplot, pointplot | среднее с доверительным интервалом |
| scatterplot, regplot | связь и регрессия |
| heatmap | матрица, например корреляций |
| pairplot | все пары столбцов |
sns.barplot(df, x='Pclass', y='Survived',
hue='пол', errorbar=('ci', 95))
Пояснение
Аргумент hue разбивает данные на группы по столбцу таблицы и сам строит легенду.
Пояснение
Грамматика графиков в духе ggplot2 из R — пакет plotnine.
01
Доли выживших по полу и классу каюты
02
barplot с 95-процентным доверительным интервалом
03
Гистограмма возраста по исходу
Что наблюдается
Выжили 97 % женщин первого класса и 14 % мужчин третьего; интервал — бутстреп с seed=0 для воспроизводимости.
root@lab — запись со стенда
Что наблюдается
Слева доли с интервалами, справа возраст: из 44 детей не старше пяти лет выжил 31, больше всего пассажиров в возрасте 20–25 лет.
Plotly, HoloViews, Bokeh и Panel: подсказки, масштаб, слайдеры, панели
06
Зачем график
Фигура и оси
Графики для статьи
Поля и палитры
Статистика
Интерактивность
import plotly.express as px
import plotly.graph_objects as go
fig = go.Figure()
fig.add_scatter(x=z, y=a,
hovertemplate='z = %{x:.2f} м<br>a = %{y:.2f} мм')
fig.write_html('envelope.html', include_plotlyjs='cdn')
px.scatter(df, x='Age', y='Fare', color='Sex')
| include_plotlyjs | Файл | Сеть |
|---|---|---|
| 'cdn' | 27 КиБ | нужен интернет |
| True | 4,6 МиБ | работает без сети |
Пояснение
Миллион точек — go.Scattergl: отрисовка переложена на видеокарту.
dim_z = hv.Dimension('z', label='Положение', unit='м')
curve = hv.Curve((z, a), dim_z, dim_a)
layout = curve + points # рядом
overlay = curve * points # в одних осях
curves = {k: hv.Curve((z, envelope(k)), dim_z, dim_a)
for k in ks}
hmap = hv.HoloMap(curves, kdims='k')
hv.save(hmap, 'scan.html', backend='bokeh')
Пояснение
Подписи осей с единицами HoloViews берёт из описания данных: «Радиус пучка (мм)».
from matplotlib.animation import FuncAnimation
line, = ax.plot(x, wave(x, 0.0))
def update(i):
line.set_ydata(wave(x, t[i]))
return (line,)
anim = FuncAnimation(fig, update, frames=200)
anim.save('wave.gif', writer='pillow', fps=30)
Пояснение
Кадр меняет данные готовых художников: set_data и set_ydata быстрее, чем новый plot на каждом шаге.
Пояснение
GIF записывает Pillow, MP4 — ffmpeg, который устанавливается отдельно.
Для физика
Эволюция волнового пакета во времени или огибающей вдоль тракта.
| Задача | Инструмент |
|---|---|
| рисунок для статьи | Matplotlib |
| разбор сырых данных | Plotly, HoloViews в Jupyter |
| скан по параметру | HoloMap, DynamicMap |
| таблица pandas в браузере | hvPlot: df.hvplot() |
| панель мониторинга установки | Panel: panel serve app.py |
Пояснение
DynamicMap пересчитывает кадр по движению слайдера и требует работающего Python; HoloMap сохраняется в HTML, работающий без Python.
Для физика
Панель мониторинга вакуума или тока пучка — страница Panel без строки JavaScript.
01
Огибающая пучка: уравнение огибающей, пять соленоидов
02
Plotly: HTML с plotly.js из сети и автономный
03
HoloViews: скан жёсткости в HTML со слайдером
04
Статичный PNG через бэкенд matplotlib
05
Размеры файлов
Что наблюдается
HTML с CDN — 26,9 КиБ, автономный — 4,6 МиБ; HoloMap из четырёх кадров — 51,5 КиБ, PNG — 78,5 КиБ. Радиус — от 2,36 до 7,13 мм.
root@lab — запись со стенда
Что наблюдается
Файл envelope-cdn.html в браузере: подсказка показывает z и a, рамка увеличивает фрагмент, двойной щелчок возвращает масштаб.
01
График — первый шаг анализа: одинаковые статистики скрывают разные данные.
02
Вид графика, масштаб и палитра выбираются по смыслу данных; подпись оси — величина и единицы.
03
Matplotlib: явные fig и ax, PNG для слайдов, PDF для статьи, размер под колонку журнала.
04
viridis — для знакопостоянных, RdBu_r с симметричными границами — для знакопеременных; без jet.
05
seaborn — статистика из таблиц, Plotly и HoloViews — интерактивный разбор данных.
01
Осциллограмма для статьи
PNG и PDF в ширину колонки 8,6 см, подписи с единицами, шрифт 9 пт
02
Профиль пучка
двумерная гауссиана на экране: imshow с extent, LogNorm, изолинии
03
Журнал установки
давление и ток из прошлой лекции на двух осях одна под другой с общей шкалой времени
04
Скан параметра
огибающая при разной жёсткости: HoloMap или Plotly со слайдером
Пояснение
Каждый рисунок проверяется на соответствие данным: пределы осей, единицы, палитра.
Для физика
Проект курса: все рисунки отчёта строит один скрипт.
1
Книга курса: глава «Визуализация на Python», приложение «Графики Matplotlib»
2
Matplotlib — документация и галерея
3
seaborn — документация
4
Plotly для Python; HoloViews и HoloViz — holoviews.org, holoviz.org
5
Anscombe F. J. Graphs in Statistical Analysis. – The American Statistician, 1973. – Vol. 27, No. 1
6
Crameri F., Shephard G. E., Heron P. J. The misuse of colour in science communication. – Nature Communications, 2020. – Vol. 11
7
Rougier N. Scientific Visualization: Python + Matplotlib. – 2021
8
Периодическая таблица методов визуализации
Вопросы
phys-dev.github.io/soft-dev-book
hv