Отображение нескольких координатных осей в одном окне

Частой задачей является отображение нескольких координатных осей (Axes) в одном окне (Figure). В самом простом варианте это можно сделать с помощью функции subplot, которая имеет следующий синтаксис:

subplot(nrows, ncols, index)

где nrows, ncols – число строк и столбцов; index – индекс текущих координатных осей. Например, мы можем разбить все пространство окна на одну строку и три столбца, получим 1 x 3 = 3 поля для отображения графиков:

import numpy as np
import matplotlib.pyplot as plt
 
plt.subplot(1, 3, 1)
plt.plot(np.random.random(10))
plt.subplot(1, 3, 2)
plt.plot(np.random.random(10))
plt.subplot(1, 3, 3)
plt.plot(np.random.random(10))
plt.grid()
plt.show()

Обратите внимание, что сетка отобразилась только для последнего графика. Почему так произошло? Дело в том, что функция grid() сработала для активного объекта Axes. А это был, как раз, третий график. Соответственно, если мы хотим установить сетку для всех осей, то, конечно, можно прописать grid() после каждого вызова функции subplot():

plt.subplot(1, 3, 1)
plt.grid()
plt.plot(np.random.random(10))
plt.subplot(1, 3, 2)
plt.grid()
plt.plot(np.random.random(10))
plt.subplot(1, 3, 3)
plt.plot(np.random.random(10))
plt.grid()
plt.show()

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

ax1 = plt.subplot(1, 3, 1)
plt.plot(np.random.random(10))
ax2 = plt.subplot(1, 3, 2)
plt.plot(np.random.random(10))
ax3 = plt.subplot(1, 3, 3)
plt.plot(np.random.random(10))

А уже потом, через эти переменные настраивать вид осей:

ax1.grid()
ax2.grid()
ax3.grid()

То есть, каждый вызов функции subplot() возвращает ссылку на создаваемый объект Axes. Причем, он создается именно в этот момент и не раньше. Например, если не создавать оси с индексом 2, то в окне будут только два графика:

Или, можно сделать так. Разбить все окно на сетку 2 x 3 и во второй строке разместить один общий график:

Работает это так. Разбиение subplot(2, 3, x) дает две строки и три столбца. Соответственно, в первой строке мы размещаем три графика. Далее, выбираем разбиение тоже в две строки, но один столбец – subplot(2, 1, x). Это позволяет нам сформировать единый график во второй строке, указывая индекс 2:

ax1 = plt.subplot(2, 3, 1)
plt.plot(np.random.random(10))
ax2 = plt.subplot(2, 3, 2)
plt.plot(np.random.random(10))
ax3 = plt.subplot(2, 3, 3)
plt.plot(np.random.random(10))
ax4 = plt.subplot(2, 1, 2)
plt.plot(np.random.random(10))

Вот так гибко можно оперировать расположениями полей для графиков. Причем, если число:

nrows < 10 и ncols < 10

то вместо трех чисел можно указывать одно трехзначное число:

ax1 = plt.subplot(131)
plt.plot(np.random.random(10))
ax2 = plt.subplot(132)
plt.plot(np.random.random(10))
ax3 = plt.subplot(133)
plt.plot(np.random.random(10))

Функция subplots()

Однако, с помощью функции subplot() не очень удобно оперировать большим числом координатных осей в пределах одной формы. Для этого лучше подходит другая похожая функция:

subplots(nrows, ncols)

которая на выходе дает ссылку на фигуру и список координатных осей:

f, ax = plt.subplots(2, 2)
plt.show()

Причем, оси здесь создаются сразу в отличие от функции subplot(). Далее, мы можем взять любой объект Axes, чтобы работать с нужным нам полем, например, так:

ax[0, 0].plot(np.arange(0, 5, 0.2))
ax[0, 0].grid()
ax[0, 1].plot(np.arange(5, 0, -0.2))
ax[0, 1].grid()

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

Объект Figure

Мало того, используя ссылку на объект Figure, можно устанавливать те или иные свойства для всего окна:

f.set_size_inches(7, 4)     # размер 7 x 4 дюймов
f.set_facecolor('#eee')     # цвет фона (светло-серый)

Полный список методов для работы с окном можно посмотреть на странице официальной документации:

https://matplotlib.org/stable/api/_as_gen/matplotlib.figure.Figure.html

Объект Figure можно создавать и независимо с помощью класса Figure, например, так:

fig = plt.figure(figsize=(7, 4))

В результате, у нас появятся два окна для отображения графиков. Это произошло по той причине, что функция subplots() автоматически создает новое окно. Если нам нужно на ранее созданной фигуре отображать графики, то мы должны сразу после создания записать функцию plot():

fig = plt.figure(figsize=(7, 4))
plt.plot(np.arange(0, 5, 0.2))

Либо, отдельно создать координатную ось и, затем, в ней отображать графики:

ax1 = fig.add_axes([0.0, 0, 1.0, 1.0])
ax1.plot(np.arange(0, 5, 0.2))

Здесь список параметров в методе add_axes() следует интерпретировать как:

[pos_x, pos_y, width, height]

Причем, все значения определяются в интервале [0; 1] и, фактически определяют долю от ширины или высоты окна. В данном случае оси занимают все пространство окна. То есть, с помощью метода add_axes() можно располагать координатные оси совершенно произвольным образом.

Другой способ добавления осей в фигуру – метод add_subplot():

fig = plt.figure(figsize=(7, 4))
ax1 = fig.add_subplot(1, 3, 1)
ax1.plot(np.arange(0, 5, 0.2))

Здесь все работает также как и с функцией subplot(), о которой мы только что говорили.

Если же нам нужно просто создать окно с одной или несколькими осями, то обычно, для этого используют уже рассмотренный нами метод:

f, ax = plt.subplots()

не указывая параметры. Тогда будет создана фигура с одной координатной осью.

Компоновка графиков с помощью GridSpec

Рассмотренные ранее подходы к компоновке графиков хороши при размещении графиков в ячейках регулярной сетки. Для более сложных элементов компоновки хорошо подходит класс GridSpec из ветки:

matplotlib.gridspec

То есть, вначале его нужно импортировать, например, так:

from matplotlib.gridspec import GridSpec

Далее, с его помощью мы также разбиваем все пространство фигуры на строки и столбцы:

fig = plt.figure(figsize=(7, 4))
gs = GridSpec(ncols=3, nrows=2, figure=fig)

И, затем, используя синтаксис срезов пакета numpy, можно формировать координатные оси, охватывающие произвольное число ячеек:

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

ax1 = plt.subplot(gs[0, 0])
ax1.plot(np.arange(0, 5, 0.2))
ax2 = fig.add_subplot(gs[1, 0:2])
ax2.plot(np.random.random(10))
ax3 = fig.add_subplot(gs[:, 2])
ax3.plot(np.random.random(10))

Причем, обратите внимание, первую ось я специально сформировал с помощью функции subplot(), а остальные две – с помощью метода add_subplot(). То есть, можно использовать любой способ формирования полей для вывода графиков.

Также можно задавать размеры каждого графика (относительно суммарной длины всех ячеек) в виде списков:

ws = [1, 2, 5]
hs = [2, 0.5]
 
gs = GridSpec(ncols=3, nrows=2, figure=fig, width_ratios=ws, height_ratios=hs)

Получим следующий результат:

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