Загальний xlabel / ylabel для субплоттів matplotlib


143

У мене такий сюжет:

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?


2
Що стосується оновлення запитання та коментарів, залишених у відповідях нижче, це дублікат stackoverflow.com/questions/6963035/…
Зачепив

Не дуже, оскільки моє запитання стосується plt.subplots (), а питання, яке ви посилаєте, використовує add_subplot - я не можу використовувати цей метод, якщо я не перейду на add_subplot, чого я хотів би уникнути. Я міг би використовувати рішення plt.text, яке подано як альтернативне рішення у вашому посиланні, але це не найвишуканіше рішення.
jolindbe

Для того, щоб зрозуміти, наскільки я розумію, plt.subplots не може генерувати набір підплотів у існуючому середовищі осі, але завжди створює нову фігуру. Правильно?
jolindbe

Найбільш елегантне рішення можна знайти тут: stackoverflow.com/questions/6963035 / ...
Mr.H

Ваше посилання надав користувач Hooked більше 4 років тому (лише кілька коментарів вище вашого). Як я вже говорив раніше, це рішення стосується add_subplot, а не plt.subplots ().
jolindbe

Відповіді:


206

Це схоже на те, що ви насправді хочете. Він застосовує той самий підхід цієї відповіді до вашого конкретного випадку:

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')

Кілька сюжетів із загальною віссю осі


4
зауважте, що 0,5 для координати x x-мітки не ставить мітку в центрі центральної підточки. вам знадобиться трохи більше, ніж це для обліку yticklabel.
dbliss

2
Подивіться на цю відповідь для методу, який не використовується plt.text. Ви створюєте свої субплоти, але потім додаєте один бітовий сюжет, робите його невидимим і позначаєте його x і y.
Джеймс Оуерс

Спасибі, працювали взагалі. Будь-яке рішення поломки при використанні tight_layout?
серв-інк

3
@ serv-inc із tight_layoutзаміною 0.04на, 0здається, працює.
divenex

3
Використовувати fig.text- це не дуже гарна ідея. Це псує такі речі, якplt.tight_layout()
мирний

55

Оскільки я вважаю це актуальним і досить елегантним (не потрібно вказувати координати для розміщення тексту), я копіюю (з невеликою адаптацією) відповідь на інше пов’язане питання .

import matplotlib.pyplot as plt
fig, axes = plt.subplots(5, 2, sharex=True, sharey=True, figsize=(6,15))
# add a big axis, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axis
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.xlabel("common X")
plt.ylabel("common Y")

Це призводить до наступного (з matplotlib версії 2.2.0):

5 рядків і 2 стовпчики підзаголовок із загальними мітками осі x і y


4
Через простоту це має бути прийнятою відповіддю. Дуже прямо. Все ще актуально для matplotlib v3.x.
Кайл Свонсон

Мені хотілося б знати, як це можна використовувати з декількома фігурними об'єктами? fig.xlabel ("foo") не працює.
Horror Vacui

FYI: Тепер, коли люди використовують темні теми в StackOverflow, мітки ледве можна прочитати, тому краще експортувати ваші png з білим тлом
xyzzyqed

@xyzzyqed Я не знав, що в stackoverflow є таке поняття, як "теми", і я навіть не пам'ятаю, як я експортував фігуру. Як я можу контролювати тло під час експорту?
блі

2
Єдина проблема цього рішення полягає в тому, що воно не працює при використанні, constrained_layout=Trueоскільки створює мітки, що перекриваються. У цьому випадку вам доведеться вручну коригувати межі підзаготівлі.
baccandr

35

Без sharex=True, sharey=Trueтебе:

введіть тут опис зображення

З ним вам слід зробити приємніше:

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()

введіть тут опис зображення

Але якщо ви хочете додати додаткові мітки, слід додати їх лише до крайових графіків:

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()

введіть тут опис зображення

Додавання мітки для кожного сюжету зіпсує це (можливо, є спосіб автоматичного виявлення повторних міток, але я про це не знаю).


Це набагато складніше, якщо, наприклад, кількість сюжетів невідома (наприклад, у вас є функція узагальнення сюжету, яка працює для будь-якої кількості субплотів).
naught101

15

З команди:

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(), щоб уникнути накладання міток.


5
Вибачте, що мені було трохи незрозуміло вище. Під "загальним" я мав на увазі одну єдину мітку x нижче всіх графіків, і одну єдину мітку y зліва від сюжетів, я оновив питання, щоб це відобразити.
jolindbe

2
@JohanLindberg: Щодо ваших коментарів тут і вище: дійсно plt.subplots()створиться новий екземпляр фігури. Якщо ви хочете дотримуватися цієї команди, ви можете легко додати a big_ax = fig.add_subplot(111), оскільки ви вже маєте фігуру і можете додати іншу вісь. Після цього ви можете маніпулювати big_axтим, як це показано у посиланні від Hooked.
Маріус

Дякую за ваші пропозиції, але якщо я це роблю, я повинен додати big_ax після plt.subplots (), і я отримую цю підпрограму поверх всього іншого - чи можу я зробити її прозорою або якось надіслати її назад? Навіть якщо я встановив всі кольори жодним, як у посиланні Hooked, це все-таки біле поле, що охоплює всі мої субплоти.
jolindbe

2
@JohanLindberg, ти маєш рацію, я цього не перевіряв. Але ви можете легко встановити колір тла великої осі none, виконуючи такі дії: big_ax.set_axis_bgcolor('none')Ви також повинні зробити labelcolor none(на відміну від прикладу, пов’язаного Hooked):big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
Marius

2
Я отримую помилку: AttributeError: 'numpy.ndarray' object has no attribute 'set_xlabel'у заяві ax.set_xlabel('Common x-label'). Ви можете це зрозуміти?
hengxin

5

Це буде виглядати краще, якщо ви зарезервуєте місце для загальних міток, зробивши невидимі мітки для субплоти в нижньому лівому куті. Також добре передавати розмір шрифту з rcParams. Таким чином, загальні мітки змінюватимуть розмір із настроюванням rc, а осі також будуть відрегульовані, щоб залишати місце для загальних міток.

fig_size = [8, 6]
fig, ax = plt.subplots(5, 2, sharex=True, sharey=True, figsize=fig_size)
# Reserve space for axis labels
ax[-1, 0].set_xlabel('.', color=(0, 0, 0, 0))
ax[-1, 0].set_ylabel('.', color=(0, 0, 0, 0))
# Make common axis labels
fig.text(0.5, 0.04, 'common X', va='center', ha='center', fontsize=rcParams['axes.labelsize'])
fig.text(0.04, 0.5, 'common Y', va='center', ha='center', rotation='vertical', fontsize=rcParams['axes.labelsize'])

введіть тут опис зображення введіть тут опис зображення


1
Гарне використання невидимої етикетки! Дякую
colelemonz

3

Я зіткнувся з подібною проблемою, будуючи графік сітки графіків. Графіки складалися з двох частин (верхньої та нижньої). 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()

картина

Недоліки:

  • Горизонтальна відстань до ділянки ґрунтується на верхній частині, нижня кліщ може поширюватися на мітку.

  • Формула не враховує місця між частинами.

  • Викидає виняток, коли висота верхньої частини дорівнює 0.

Мабуть, є загальне рішення, яке враховує прокладку між фігурами.


Гей, я придумав спосіб зробити це дуже в дусі вашої відповіді, але, можливо, вирішимо деякі з цих питань; дивіться stackoverflow.com/a/44020303/4970632 (нижче)
Люк Девіс

2

Оновлення:

Ця функція тепер є частиною пакету proplot matplotlib, який я нещодавно випустив на pypi. За замовчуванням, коли ви робите фігури, мітки "розділяються" між осями.


Оригінальна відповідь:

Я виявив більш надійний метод:

Якщо ви знаєте, bottomі topkwargs, які пішли в GridSpecініціалізацію, або ви іншим чином знаєте положення країв своїх осей за Figureкоординатами , ви також можете вказати положення ylabel в 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-координати, не перевіряючи, чи змінилася основна трансформація координат.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.