Рисуем линии уровня функциями contour, contourf и tricontour, tricontourf

Трехмерные графики, которые мы подробно рассмотрели на прошлом занятии, иногда изображают в виде линий уровня:

Для этого используются функции:

  • contour() и contourf() – если данные по осям x, y, z представлены в виде двумерных массивов;
  • tricontour() и tricontourf() – если данные для x, y, z представлены одномерными массивами.

Например, если взять программу из предыдущего занятия, которая рисовала синусоиду:

import numpy as np
import matplotlib.pyplot as plt
 
fig = plt.figure(figsize=(7, 4))
ax_3d = fig.add_subplot(projection='3d')
 
x = np.arange(-2*np.pi, 2*np.pi, 0.2)
y = np.arange(-2*np.pi, 2*np.pi, 0.2)
xgrid, ygrid = np.meshgrid(x, y)
 
zgrid = np.sin(xgrid) * np.sin(ygrid) / (xgrid * ygrid)
 
ax_3d.plot_surface(xgrid, ygrid, zgrid)

Но вместо функции plot_surface() записать функцию contour() или contourf(), то получим следующий результат:

ax_3d.contour(xgrid, ygrid, zgrid)

Или, те же самые функции можно применить для формирования двумерного графика. Вначале создадим двумерную систему координат:

fig, ax = plt.subplots(1, 2)

а, затем, вызовем эти же функции, используя переменную ax:

ax[0].contour(xgrid, ygrid, zgrid)
ax[1].contourf(xgrid, ygrid, zgrid)

В обоих вариантах видим двумерную синусоиду, представленную линиями уровней.

Функции contour() и contourf() возвращают объект класса QuadContourSet (это дочерний класс от базового ContourSet). Так вот, имея ссылку на экземпляр этого класса, можно осуществлять дополнительные манипуляции при отображении графика. Например, добавим к каждой линии ее числовое значение. Для этого, вначале сохраним ссылку на объект класса QuadContourSet:

c1 = ax[0].contour(xgrid, ygrid, zgrid)

А, затем, передадим ее функции clabel():

ax[0].clabel(c1)

В результате, на первом графике появятся подписи:

Того же результата можно достичь, вызвав метод clabel() непосредственно через переменную c1:

c1.clabel()

Подробное описание этого метода можно посмотреть по ссылке:

https://matplotlib.org/stable/api/contour_api.html#matplotlib.contour.ContourLabeler.clabel

Четвертым параметром в функциях contour() и contourf() можно указывать либо число линий уровня:

fig, ax = plt.subplots()
 
x = np.arange(-2*np.pi, 2*np.pi, 0.2)
y = np.arange(-2*np.pi, 2*np.pi, 0.2)
xgrid, ygrid = np.meshgrid(x, y)
 
zgrid = np.sin(xgrid) * np.sin(ygrid) / (xgrid * ygrid)
 
c1 = ax.contour(xgrid, ygrid, zgrid, 15)
c1.clabel()
 
plt.show()

Либо передавать список уровней, которые мы бы хотели отобразить для заданного графика:

c1 = ax.contour(xgrid, ygrid, zgrid, [-0.5, -0.2, 0, 0.2, 0.5])

Далее, для линий уровня можно прописать необходимые цвета, либо через параметр colors:

c1 = ax.contour(xgrid, ygrid, zgrid, colors='g')

либо через список:

c1 = ax.contour(xgrid, ygrid, zgrid, colors=['g', 'b', 'r'])

А также с помощью цветовых карт, используя параметр cmap:

c1 = ax.contour(xgrid, ygrid, zgrid, cmap='plasma')

Для надписей цвета можно задавать отдельно в функции clabel():

c1.clabel(colors='k')

Теперь все подписи отображаются черным цветом. Еще один полезный параметр fmt этой функции позволяет задавать формат отображаемых чисел. Например, так:

c1.clabel(colors='k', fmt='%.2f')

Функции tricontour() и tricontourf()

Похожим образом работают и функции tricontour() и tricontourf(), только на входе они принимают одномерные векторы координат x, y, z. Причем, координаты x, y можно сформировать совершенно случайным образом, например, так:

x = np.random.rand(100) * 4*np.pi - 2*np.pi
y = np.random.rand(100) * 4*np.pi - 2*np.pi

Здесь мы имеем по 100 случайных значений в диапазоне [-2π; 2π]. То есть, сетка в плоскости xy уже не регулярная, а произвольная. Затем, для этих чисел вычисляется 100 значений функции синуса:

z = np.sin(x) * np.sin(y) / (1+np.abs(x * y))

И с помощью функции tricontour() отображается график с линиями уровня:

c1 = ax.tricontour(x, y, z, cmap='plasma')
c1.clabel(colors='k', fmt='%.2f')

Похожий результат достигается второй функцией tricontourf():

c1 = ax.tricontourf(x, y, z, cmap='plasma')

Вот так, довольно просто, можно строить график с линями уровней, используя четыре функции:

contour(), contourf() и tricontour(), tricontourf()

Конечно, это не все возможности пакета matplotlib по отображению таких графиков. Более детальную информацию, как всегда, можно посмотреть в документации.