Общая xlabel/ylabel для подзаголовков matplotlib

У меня есть следующий график:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

и теперь я хотел бы дать этому графику общие метки оси x и метки оси Y. С "общим" я имею в виду, что должна быть одна большая метка оси x под всей сеткой подзаголовков, а одна большая метка оси Y справа. Я не могу найти ничего об этом в документации для plt.subplots, и мои googlings предполагают, что мне нужно сделать большой plt.subplot(111) для начала, но как я тогда помещаю свои подстроки 5 * 2 в это, используя plt.subplots?

Ответ 1

Это похоже на то, что вы на самом деле хотите. Он применяет тот же подход этого ответа к вашему конкретному случаю:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(nrows=3, ncols=3, sharex=True, sharey=True, figsize=(6, 6))

fig.text(0.5, 0.04, 'common X', ha='center')
fig.text(0.04, 0.5, 'common Y', va='center', rotation='vertical')

Multiple plots with common axes label

Ответ 2

Без sharex=True, sharey=True вы получите:

enter image description here

С ним вы должны получить его лучше:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))

plt.tight_layout()

enter image description here

Но если вы хотите добавить дополнительные метки, вы должны добавить их только к граничным графикам:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))
        if i == len(axes2d) - 1:
            cell.set_xlabel("noise column: {0:d}".format(j + 1))
        if j == 0:
            cell.set_ylabel("noise row: {0:d}".format(i + 1))

plt.tight_layout()

enter image description here

Добавление метки для каждого графика испортит ее (возможно, есть способ автоматически обнаруживать повторяющиеся метки, но я не знаю об этом).

Ответ 3

Так как команда:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

который вы использовали, возвращает кортеж, состоящий из фигуры и списка экземпляров осей, уже достаточно сделать что-то вроде (помните, что я изменил fig,ax на fig,axes):

fig,axes = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

for ax in axes:
    ax.set_xlabel('Common x-label')
    ax.set_ylabel('Common y-label')

Если вам захочется изменить некоторые детали на конкретном подзаголовке, вы можете получить к нему доступ через axes[i], где i выполняет итерацию по вашим подзаголовкам.

Также может быть очень полезно включить

fig.tight_layout()

в конце файла перед plt.show(), чтобы избежать перекрытия меток.

Ответ 4

У меня возникла аналогичная проблема при построении сетки графиков. Графики состояли из двух частей (сверху и снизу). Предполагалось, что y-метка будет центрирована по обеим частям.

Я не хотел использовать решение, которое зависит от знания положения на внешней фигуре (например, fig.text()), поэтому я манипулировал y-позицией функции set_ylabel(). Это обычно 0,5, середина графика, к которой он добавлен. Поскольку заполнение между частями (hspace) в моем коде было нулевым, я мог рассчитать середину двух частей относительно верхней части.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

# Create outer and inner grid
outerGrid = gridspec.GridSpec(2, 3, width_ratios=[1,1,1], height_ratios=[1,1])
somePlot = gridspec.GridSpecFromSubplotSpec(2, 1,
               subplot_spec=outerGrid[3], height_ratios=[1,3], hspace = 0)

# Add two partial plots
partA = plt.subplot(somePlot[0])
partB = plt.subplot(somePlot[1])

# No x-ticks for the upper plot
plt.setp(partA.get_xticklabels(), visible=False)

# The center is (height(top)-height(bottom))/(2*height(top))
# Simplified to 0.5 - height(bottom)/(2*height(top))
mid = 0.5-somePlot.get_height_ratios()[1]/(2.*somePlot.get_height_ratios()[0])
# Place the y-label
partA.set_ylabel('shared label', y = mid)

plt.show()

картинка

Downsides:

  • Горизонтальное расстояние до графика основано на верхней части, нижние тики могут распространяться на метку.

  • Формула не учитывает пробел между частями.

  • Выдает исключение, если высота верхней части равна 0.

Возможно, существует общее решение, которое учитывает отступы между фигурами.

Ответ 5

Я обнаружил альтернативный метод; если вы знаете bottom и top kwargs, которые вошли в инициализацию GridSpec, или вы иначе знаете положения краев ваших осей в координатах Figure, вы также можете указать ярлык положение в координатах Figure с некоторой фантастической магией "преобразования". Например:

import matplotlib.transforms as mtransforms
bottom, top = .1, .9
f, a = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = (bottom+top)/2
a[0].yaxis.label.set_transform(mtransforms.blended_transform_factory(
       mtransforms.IdentityTransform(), f.transFigure # specify x, y transform
       )) # changed from default blend (IdentityTransform(), a[0].transAxes)
a[0].yaxis.label.set_position((0, avepos))
a[0].set_ylabel('Hello, world!')

... и вы должны увидеть, что метка по-прежнему соответствующим образом регулирует влево-вправо, чтобы не перекрывать метки с метками, как обычно, но теперь она будет регулироваться всегда точно между желаемыми подзаголовками.

Кроме того, если вы даже не используете set_position, ylabel будет отображаться по умолчанию точно на полпути вверх по фигуре. Я предполагаю, что это происходит потому, что, когда ярлык окончательно нарисован, matplotlib использует 0,5 для y -координата, не проверяя, изменилось ли базовое преобразование координат.