Графики и карты#
Самым популярным модулем для построения графиков в Python является matplotlib
мы будем рассматривать именно её, но есть и другие, в частности plotnine
и plotly
.
Глобальный холст pyplot, объекты Figure и Axis#
Бекенды#
Модуль matplotlib
предоставляет стандартное API для построение графиков и, так называемые, бекенды, которые отвечают за вывод результатов пользователю.
Они бывают как интерактивные (открываются в отдельном окне, позволяют заимодействовать с графиком), так и неинтерактивные, результатом работы которых является растровое или вектороное изображение. В средах использующих IPython (например Spyder и Jupyter) имеется дополнительный бекенд inline
, для вывода графиков в среду. Неинтерактивные бекенды удобны для создания большого числа графиков.
Выбор бекенда осуществляется в настройках среды или в коде:
import matplotlib
Глобальный объект pyplot#
Модуль matplotlib
содержит глобальный объект pyplot
, который выступает в роли стандартного холста и в большинстве примеров импортируется как plt
:
import numpy as np
import matplotlib.pyplot as plt
# рисуем
plt.plot([1,2,3],[5,3,7])
# отображаем или сохраняем
plt.show()
# plt.savefig('filename.png')
Для сохранение графиков целесообразно использовать векторные форматы, такие как: .svg
, .eps
или растровые без потерь: .png
, .tiff
.
При запуске по отдельным ячейкам среда (например Spyder или Jupyter) автоматически вызывает plt.show()
, если в ячейке были вызовы функций рисования, но результат не был отображен или сохранен явно (через show()
или savefig()
).
После отображения или сохранения изображения в файл глобальный объект pyplot
очищается.
Рисование с использованием объекта plt#
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0+0.01, 0.01)
v = 1 + np.sin(2*np.pi*t)
plt.plot(t, v, color="blue", label="v(t)")
plt.xlabel('Время, с')
plt.ylabel('Значение, мВ')
plt.title('Зависимость напряжения от времени')
plt.grid()
plt.legend()
plt.show()
Рисование с использованием объектов Axis и Figure#
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0+0.01, 0.01)
v = 1 + np.sin(2*np.pi*t)
fig = plt.figure()
ax = fig.add_subplot()
ax.plot(t, v, color="blue", label="v(t)")
ax.set_xlabel('Время, с')
ax.set_ylabel('Значение, мВ')
ax.set_title('Зависимость напряжения от времени')
ax.grid()
ax.legend();
Построение графиков X-Y#
Анатомия графика#
Популярные типы графиков для точек#
Метод |
Назначение |
---|---|
|
Кривая |
|
Точки |
|
Точки с усами ошибок |
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0+0.01, 0.01)
v = 1 + np.sin(2*np.pi*t)
t2 = np.arange(0.0, 2.0+0.1, 0.1)
v2 = 1 + np.sin(2*np.pi*t2) + np.random.random(t2.shape)-0.5
fig = plt.figure()
ax = fig.add_subplot()
ax.plot(t, v, color="blue", label="v(t)")
ax.scatter(t2, v2, color="red", label="v[t]", zorder=2.5)
ax.errorbar(t2, v2, 0.5 + 0.5*(np.random.random(t2.shape)-0.5), linestyle="", color="red", capsize=2)
ax.set_xlabel('Время, с')
ax.set_ylabel('Значение, мВ')
ax.set_title('Зависимость напряжения от времени')
ax.grid()
ax.legend();
Некоторые аргументы plot и scatter#
Аргумент |
Назначение |
Примеры значений |
---|---|---|
|
Цвет линии |
|
|
Текст в легенде |
|
|
Стиль линии |
|
|
Ширина линии |
|
|
Форма маркера |
|
|
Цвет маркера |
|
|
Цвет обводки маркера |
|
|
Ширина обводки маркера |
|
|
Размер маркера |
|
Вспомогательные функции - надписи и подписи#
Функция |
Назначение |
Пример использования |
---|---|---|
|
Подпись оси X |
|
|
Подпись оси Y |
|
|
Заголовок графика |
|
|
Показать легенду |
|
|
Добавить аннотацию к точке xy |
|
В тексте можно использовать команды на TeXе окружая их знаками долларов: $4\pi^2$
.
Вспомогательные функции - оси#
Функция |
Назначение |
Пример использования |
---|---|---|
|
Границы по оси X |
|
|
Границы по оси Y |
|
|
Отсчеты по оси X |
|
|
Отсчеты по оси Y |
|
|
Включить сетку |
|
|
Горизонтальные линии |
|
|
Вертикальные линии |
|
Линия тренда#
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot()
x = np.random.random(100)
y = 3*x + x*x*np.random.normal(1, 0.5, x.shape)
ax.scatter(x, y, alpha=0.5)
m, b = np.polyfit(x, y, deg=1)
ax.axline(xy1=(0, b), slope=m, color='r', label=f'$y = {m:.2f}x {b:+.2f}$')
a2, a1, a0 = np.polyfit(x, y, deg=2)
def foo(x):
return a2*x**2 + a1*x + a0
q = np.linspace(0,1,20)
ax.plot(q,foo(q), color='b', label=f'$y = {a2:.2f}x^2 {a1:+.2f}x {a0:+.2f}$')
plt.legend()
plt.show()
Построение теплокарт и контуров#
Метод |
Назначение |
Пример |
---|---|---|
|
Рисование теплокарты |
|
|
Рисование контуров (линии) |
|
|
Рисование контуров (заливка) |
|
Для построения теплокарты необходимо иметь двумерный массив с данными и его extent
- привязку углов массива к координатам.
data = np.random.random((6,6))
data[0,0] = 2
fig = plt.figure()
ax = fig.add_subplot()
im = ax.imshow(data, extent=[-3, 3, -3, 3], vmin=-1, vmax=2, origin='lower', cmap='YlGnBu')
fig.colorbar(im);
Для построения контуров необходимо иметь: два одномерных массива с равномерными интервалами между точками и двумерный массив с данными. Ключевые методы plt.contour
и plt.contourf
.
# Источник данных — функция двух переменных
def f(x, y):
return (1 - x / 2 + x ** 5 + y ** 3) * np.exp(-x ** 2 -y ** 2)
# Создаем массивы для осей:
n = 256
x = np.linspace(-3, 3, n)
y = np.linspace(-3, 3, n)
# Делаем мешгрид
gridX, gridY = np.meshgrid(x, y)
data = f(gridX, gridY)
#Раскрашивание контуров цветами (палитра 'jet')
cntrf = plt.contourf(gridX, gridY, data, 8, alpha=.75, cmap='jet')
#Разграничение контуров линиями
cntr = plt.contour(gridX, gridY, data, 8, colors='black')
#Добавляем подписи на контуры
plt.clabel(cntr, inline=1, fontsize=10)
#Добавляем цветовую легенду
plt.colorbar(cntrf);
Управление расположением цветовой шкалы#
from mpl_toolkits.axes_grid1 import make_axes_locatable
data = np.random.random((6,6))
data[0,0] = 2
fig = plt.figure()
ax = fig.add_subplot()
im = ax.imshow(data, extent=[-3, 3, -3, 3], vmin=-2, vmax=2, origin='lower', cmap='magma')
# находим нашу систему коордит в рамках Figure
divider = make_axes_locatable(ax)
# определяем новую область для рисования цветовой шкалы
color_ax = divider.append_axes("bottom", size="8%", pad="10%")
# добавляем к фигуре новую область для цветовой шкалы
fig.add_axes(color_ax)
#рисуем шкалу
fig.colorbar(im, cax=color_ax, orientation="horizontal");
Построение гистограмм и столбчатых диаграмм#
Метод |
Назначение |
Пример |
---|---|---|
|
Гистограмма |
|
|
Столбчатая диаграмма |
|
data = np.random.normal(0,1,300)
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(1,2,1)
# Простая гистограмма
ax.hist(data, bins=10, color="red");
# Расчитанная гистограмма, hist - значение в столбце, bin_edges - границы столбцов
hist, bin_edges = np.histogram(data, bins=20)
ax.bar(bin_edges[:-1], hist, 0.25, color="blue")
import scipy
# Ядерная оценка плотности
kde = scipy.stats.gaussian_kde(data)
xtx = np.linspace(-3,3,100)
ax2 = fig.add_subplot(1,2,2)
ax2.plot(xtx, kde(xtx), color="purple", linewidth=3);
t = np.arange(0.0, 1.0+0.1, 0.1)
v = 1 + np.sin(2*np.pi*t)
fig = plt.figure()
ax = fig.add_subplot()
ax.bar(t,v,0.09);
Специальные виды графиков#
Диаграммы потоков#
import numpy as np
from findiff import FinDiff, Gradient, Divergence, Laplacian
import matplotlib.pyplot as plt
borders = [-2*np.pi, 2*np.pi, -2*np.pi, 2*np.pi]
x = np.linspace(-2*np.pi, 2*np.pi, 30)
dx = x[1]-x[0]
y = np.linspace(-2*np.pi, 2*np.pi, 30)
dy = y[1]-y[0]
X, Y = np.meshgrid(x, y, indexing='ij')
f = Y*np.sin(1/2*X) + np.cos(Y/4)
grad = Gradient(h=[dx, dy])
grad_f = grad(f)
fig = plt.figure(figsize=(12,4))
ax = fig.add_subplot(1,2,1)
im = ax.imshow(f.T , origin='lower', extent=borders, cmap="magma_r")
fig.colorbar(im)
ax.quiver(X,Y,grad_f[0],grad_f[1], color="green")
d_dx = FinDiff(0, dx, 1)
d_dy = FinDiff(1, dy, 1)
ax2 = fig.add_subplot(1,2,2)
ax2.streamplot(x,y,grad_f[0].T,grad_f[1].T);
Логорифмические оси и полярные координаты#
Функция |
Назначение |
Пример использования |
---|---|---|
|
Масштаб по оси X |
|
|
Масштаб по оси Y |
|
Создать систему полярных координат |
|
t = np.linspace(0.0, 1.5, 50)
v = 1 + np.sin(2*np.pi*t)
fig = plt.figure(figsize=(9,3))
ax = fig.add_subplot(1,2,1)
ax.plot(t, v)
ax.grid(True, which='both');
ax = fig.add_subplot(1,2,2)
ax.plot(t, v)
ax.set_xscale('log')
ax.set_yscale('log')
ax.grid(True, which='both');
import matplotlib.pyplot as plt
import numpy as np
r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
fig = plt.figure(figsize=(9,3))
ax1 = fig.add_subplot(1,2,1)
ax1.plot(theta, r)
ax1.grid(True);
ax2 = fig.add_subplot(1,2,2, projection='polar')
ax2.plot(theta, r)
ax2.set_rmax(2)
ax2.set_rticks([0.5, 1, 1.5, 2])
ax2.grid(True);
Несколько систем координат на одном рисунке#
Для построения несолькоих систем координат будем использовать matplotlib.gridspec
.
# Задаем сетку для деления 2 строки 3 столбца
grsp = matplotlib.gridspec.GridSpec(2, 3)
fig = plt.figure()
# Получаем указатели на отельные оси с помощью слайсов
axis1 = fig.add_subplot(grsp[0, :]) # Вся верхняя строка
axis2 = fig.add_subplot(grsp[1, 0:2]) # Нижняя строка две левых клетки
axis3 = fig.add_subplot(grsp[1, 2]) # Нижняя строка правый угол
t = np.arange(0.0, 1.0+0.1, 0.01)
v = 1 + np.sin(2*np.pi*t)
axis1.plot(t,v)
axis2.plot(t,v)
axis3.plot(t,v)
axis3.set_yticks([]);
Карты#
Раньше для построения карт использовался модуль basemap
, но он устарел и более не поддерживается. На замену ему пришел модуль cartopy
.
Для выбора нужной проекции используем документацию.
Рисование данных поверх карты выполняется обычными методами, например plot с добавлением параметра transform если координаты указаны, как долгота и широта в градусах.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cartopy import crs as ccrs
fig = plt.figure()
ax = fig.add_subplot(projection=ccrs.PlateCarree())
ax.set_extent([-6, 3, 48, 58], crs=ccrs.PlateCarree())
gl = ax.gridlines(draw_labels=True)
gl.xlocator = mticker.FixedLocator(np.arange(-6.0,3.0,2.0))
gl.ylocator = mticker.FixedLocator(np.arange(48.0,58.0,2.0))
ax.scatter([-2],[52], transform=ccrs.PlateCarree())
ax.coastlines(resolution='50m');
Картографические данные Natural Earth#
Картографические данные cartopy берет из проекта Natural Earth.
Какие именно картографические данные доступны см. [по ссылке] (https://github.com/nvkelso/natural-earth-vector) в наборах cultural
и physical
. Карты автоматически скачиваются по мере надобности, при этом в консоль выводится предупреждение DownloadWarning.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cartopy import crs as ccrs
import cartopy.feature
fig = plt.figure()
ax = fig.add_subplot(projection=ccrs.PlateCarree())
ax.set_extent([-6, 3, 38, 48], crs=ccrs.PlateCarree())
gl = ax.gridlines(draw_labels=True)
gl.xlocator = mticker.FixedLocator(np.arange(-6.0,3.0,2.0))
gl.ylocator = mticker.FixedLocator(np.arange(38.0,48.0,2.0))
resol = '50m' # еще есть '110m' - самая грубая версия и '10m' - самая точная версия
bodr = cartopy.feature.NaturalEarthFeature(category='cultural',
name='admin_0_boundary_lines_land', scale=resol, facecolor='none', alpha=0.7)
land = cartopy.feature.NaturalEarthFeature('physical', 'land', \
scale=resol, edgecolor='k', facecolor=cartopy.feature.COLORS['land'])
ocean = cartopy.feature.NaturalEarthFeature('physical', 'ocean', \
scale=resol, edgecolor='none', facecolor=cartopy.feature.COLORS['water'])
lakes = cartopy.feature.NaturalEarthFeature('physical', 'lakes', \
scale=resol, edgecolor='b', facecolor=cartopy.feature.COLORS['water'])
rivers = cartopy.feature.NaturalEarthFeature('physical', 'rivers_lake_centerlines', \
scale=resol, edgecolor='b', facecolor='none')
ax.add_feature(land)
ax.add_feature(ocean)
ax.add_feature(lakes)
ax.add_feature(rivers)
ax.add_feature(bodr, linestyle='--', alpha=1);
Использование Tile-серверов в cartopy#
import matplotlib.pyplot as plt
from cartopy import crs as ccrs
from cartopy.io.img_tiles import GoogleWTS, OSM, GoogleTiles
def make_map_from_url(url):
class URLMAP(GoogleWTS):
def _image_url(self, tile):
x, y, z = tile
return url.format(x=x,y=y,z=z)
return URLMAP()
#Спутниковые снимки
#Спутник ESRI
#tiles_provider = make_map_from_url('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}')
#Спутник Yandex
#tiles_provider = make_map_from_url('https://core-sat.maps.yandex.net/tiles?l=sat&x={x}&y={y}&z={z}')
#Карты
#OpenStreetMap
tiles_provider = OSM()
#OpenStreetMap CycloSM
#tiles_provider = make_map_from_url('https://a.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png')
#Карта Yandex
#tiles_provider = make_map_from_url('https://core-renderer-tiles.maps.yandex.net/tiles?l=map&x={x}&y={y}&z={z}&scale=2&lang=ru-RU')
#Карта Google
#tiles_provider = GoogleTiles()
#Карта ESRI Street
#tiles_provider = make_map_from_url('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}')
#Карта ESRI Topo
#tiles_provider = make_map_from_url('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}')
#TopoMapper
#tiles_provider = make_map_from_url('https://proxy.nakarte.me/http/88.99.52.155/cgi-bin/tapp/tilecache.py/1.0.0/topomapper_v2/{z}/{x}/{y}.jpg')
#Без глобального охвата
#Генштаб 10km
#tiles_provider = make_map_from_url('https://c.tiles.nakarte.me/topo001m/{z}/{x}/{y}')
#Генштаб 1km
#tiles_provider = make_map_from_url('https://b.tiles.nakarte.me/topo001m/{z}/{x}/{y}')
#Прочее от ESRI
#Рельеф с тенями
#tiles_provider = make_map_from_url('https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}')
#Суша с тенями
#tiles_provider = make_map_from_url('https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}')
#Физическая карта
#tiles_provider = make_map_from_url('https://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}')
plt.figure(figsize=(10, 10))
ax = plt.axes(projection=tiles_provider.crs)
ax.set_extent([-6, 3, 38, 48], ccrs.PlateCarree())
ax.add_image(tiles_provider, 6);
Некоторые приемы работы с cartopy#
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cartopy import crs as ccrs
fig = plt.figure()
ax = fig.add_subplot(projection=ccrs.PlateCarree())
ax.set_extent([-6, 3, 48, 58], crs=ccrs.PlateCarree())
# Скрытие части подписей по осям:
gridlines = ax.gridlines(draw_labels=True)
gridlines.top_labels = False
gridlines.right_labels = False
# Добавление теплокарты:
data = np.random.random((6,6))
im = ax.imshow(data, origin='upper', extent =[-6, 3, 48, 58], transform=ccrs.PlateCarree())
fig.colorbar(im, ax=ax)
ax.coastlines(resolution='50m')
#Добавление точки с подписью:
chords = np.array([[0.1],[51.5]])
ax.scatter(chords[0],chords[1], color="red", transform=ccrs.PlateCarree())
mpl_trans = ccrs.PlateCarree()._as_mpl_transform(ax)
ax.annotate('LONDON',(chords[0,0]+0.2,chords[1,0]+0.2), color="red", xycoords=mpl_trans);