метки осей pyplot для подзаголовков

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

import matplotlib.pyplot as plt

fig2 = plt.figure()
ax3 = fig2.add_subplot(2,1,1)
ax4 = fig2.add_subplot(2,1,2)
ax4.loglog(x1, y1)
ax3.loglog(x2, y2)
ax3.set_ylabel('hello')

Я хочу иметь возможность создавать метки и заголовки осей не только для каждого из двух подзаголовков, но и общие метки, которые охватывают оба подзаголовка. Например, поскольку оба графика имеют идентичные оси, мне нужен только один набор меток осей x и y. Однако мне нужны разные названия для каждого сюжета.

Я пробовал несколько вещей, но ни одно из них не помогло


person farqwag25    schedule 05.08.2011    source источник


Ответы (7)


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

import random
import matplotlib.pyplot as plt

x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]

fig = plt.figure()
ax = fig.add_subplot(111)    # The big subplot
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

# Turn off axis lines and ticks of the big subplot
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.tick_params(labelcolor='w', top=False, bottom=False, left=False, right=False)

ax1.loglog(x, y1)
ax2.loglog(x, y2)

# Set common labels
ax.set_xlabel('common xlabel')
ax.set_ylabel('common ylabel')

ax1.set_title('ax1 title')
ax2.set_title('ax2 title')

plt.savefig('common_labels.png', dpi=300)

common_labels.png

Другой способ - использовать fig.text () для непосредственной установки расположения общих меток.

import random
import matplotlib.pyplot as plt

x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]

fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

ax1.loglog(x, y1)
ax2.loglog(x, y2)

# Set common labels
fig.text(0.5, 0.04, 'common xlabel', ha='center', va='center')
fig.text(0.06, 0.5, 'common ylabel', ha='center', va='center', rotation='vertical')

ax1.set_title('ax1 title')
ax2.set_title('ax2 title')

plt.savefig('common_labels_text.png', dpi=300)

common_labels_text.png

person wwliao    schedule 08.08.2011
comment
Функция suptitle использует версию fig.text (). Так может быть это официальный способ сделать это? - person PhML; 26.03.2013
comment
Стоит подчеркнуть, что ax должен быть создан до ax1 и ax2, иначе большой сюжет закроет маленькие сюжеты. - person 1''; 30.11.2014
comment
ax.grid (False) или plt.grid (False) также необходимы, если глобальные параметры построения включают (видимую) сетку. - person Næreen; 17.10.2017
comment
Кажется, что первый подход больше не работает с последними версиями matplotplib (я использую 2.0.2): метки, добавленные к включающему топору, не видны. - person M. Toya; 27.10.2017
comment
Как добавить y_labels к каждому отдельному подзаголовку? - person Fardin Abdi; 24.05.2018
comment
Спасибо! fig.text(0.06, 0.5, 'common ylabel', ha='center', va='center', rotation='vertical') работал у меня. - person Kareem Jeiroudi; 27.03.2019

Один простой способ с использованием subplots:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(3, 4, sharex=True, sharey=True)
# add a big axes, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axes
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.grid(False)
plt.xlabel("common X")
plt.ylabel("common Y")
person Julian Chen    schedule 11.04.2016
comment
ax.grid (False) или plt.grid (False) также необходимы, если глобальные параметры построения включают (видимую) сетку. - person Næreen; 17.10.2017
comment
labelcolor='none', по крайней мере, не отключает метки для вывода pgf. Вы можете заставить его работать с labelcolor=(1, 1, 1) (если фон белый). Но я думаю, они все еще там, например, если вы выберете их в выводе, вы можете их скопировать. - person asmeurer; 11.11.2017
comment
Добавление аргумента which="both" в строку plt.tick_params() помогло мне удалить основные оставшиеся отметки. - person ZYX; 12.02.2018
comment
Если вам нужны и общие метки, и метки подзаголовков, вы можете добавить некоторый интервал между ними, чтобы они не перекрывались. plt.ylabel("Common Y", labelpad=20) - person dankal444; 25.10.2018
comment
Я делаю это для подзаголовка (5, 1), и моя ylabel находится далеко от левого края окна, а не рядом с подзаголовками. - person Evidlo; 10.04.2019
comment
Вы получили положительный голос. но, пожалуйста, всегда объясняйте, что делает код, прикрепляйте изображение или показывайте пример, потому что для его получения определенно потребовалось немного времени. - person Kareem Jeiroudi; 18.04.2019
comment
sharey=True, sharex=True не особо нужны, иначе аккуратный ответ! - person pcko1; 04.07.2019
comment
Измените 'off' на False с более новыми версиями Matplotlib (у меня 2.2.2) - person Ted; 07.08.2019
comment
А как потом добавлять сюжеты? for ax in axes: ax.plot(x, y), похоже, не приносит никакой пользы. - person usernumber; 16.10.2019
comment
Это хороший ответ, обратите внимание на дополнительную панель для надписей, в частности, для команды ylabel. Это необходимо, например, когда подзаголовки имеют логарифмический масштаб, чтобы немного отодвинуть метку от оси. Точно так же вы можете дать отрицательное число, если чувствуете, что оно расположено слишком далеко от осей. - person yngve; 30.10.2019
comment
У меня есть несколько перекрывающихся точек x из подзаговора (111) и других моих подзаголовков. Я использовал plt.tick_params (labelcolor = none, bottom = False, left = False) после fig.add_subplot (111, frame_on = False) из kite.com/python/answers/ - person Surya Narayanan; 11.07.2021

plt.setp() выполнит свою работу:

# plot something
fig, axs = plt.subplots(3,3, figsize=(15, 8), sharex=True, sharey=True)
for i, ax in enumerate(axs.flat):
    ax.scatter(*np.random.normal(size=(2,200)))
    ax.set_title(f'Title {i}')

# set labels
plt.setp(axs[-1, :], xlabel='x axis label')
plt.setp(axs[:, 0], ylabel='y axis label')

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

person MohammadReza    schedule 03.03.2020
comment
Есть ли способ также установить размер / вес шрифта с помощью этого метода? - person pfabri; 10.07.2020
comment
@pfabri ... plt.setp(axs[-1, :], xlabel='x axis label') изменяет только текст метки (очевидно). если вы хотите настроить его текстовые параметры, сделайте, например, ax.set_xlabel(None, size=12, weight='demibold', color='xkcd:lime green', labelpad=0.33) внутри цикла for. проверьте matplotlib.text.Text < / a> подробнее **kwargs. - person Manuel F; 23.01.2021

Ответ Wen-wei Liao хорош, если вы не пытаетесь экспортировать векторную графику или если вы настроили бэкенды matplotlib для игнорирования бесцветных осей; в противном случае скрытые оси будут отображаться в экспортированном графике.

Мой ответ suplabel здесь аналогичен ответу fig.suptitle, который использует функцию fig.text. Следовательно, нет художника-топора, который создается и обесцвечивается. Однако, если вы попытаетесь вызвать его несколько раз, вы получите текст, добавленный один поверх другого (как и fig.suptitle). Ответ Вэнь-вэй Ляо - нет, потому что fig.add_subplot(111) вернет тот же объект Axes, если он уже создан.

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

def suplabel(axis,label,label_prop=None,
             labelpad=5,
             ha='center',va='center'):
    ''' Add super ylabel or xlabel to the figure
    Similar to matplotlib.suptitle
    axis       - string: "x" or "y"
    label      - string
    label_prop - keyword dictionary for Text
    labelpad   - padding from the axis (default: 5)
    ha         - horizontal alignment (default: "center")
    va         - vertical alignment (default: "center")
    '''
    fig = pylab.gcf()
    xmin = []
    ymin = []
    for ax in fig.axes:
        xmin.append(ax.get_position().xmin)
        ymin.append(ax.get_position().ymin)
    xmin,ymin = min(xmin),min(ymin)
    dpi = fig.dpi
    if axis.lower() == "y":
        rotation=90.
        x = xmin-float(labelpad)/dpi
        y = 0.5
    elif axis.lower() == 'x':
        rotation = 0.
        x = 0.5
        y = ymin - float(labelpad)/dpi
    else:
        raise Exception("Unexpected axis: x or y")
    if label_prop is None: 
        label_prop = dict()
    pylab.text(x,y,label,rotation=rotation,
               transform=fig.transFigure,
               ha=ha,va=va,
               **label_prop)
person KYC    schedule 17.03.2015
comment
Это лучший ответ имо. Это легко реализовать, и метки не перекрываются из-за опции labelpad. - person Arthur Dent; 25.06.2018

Вот решение, в котором вы устанавливаете ylabel одного из графиков и настраиваете его положение, чтобы оно было центрировано по вертикали. Таким образом вы избежите проблем, упомянутых KYC.

import numpy as np
import matplotlib.pyplot as plt

def set_shared_ylabel(a, ylabel, labelpad = 0.01):
    """Set a y label shared by multiple axes
    Parameters
    ----------
    a: list of axes
    ylabel: string
    labelpad: float
        Sets the padding between ticklabels and axis label"""

    f = a[0].get_figure()
    f.canvas.draw() #sets f.canvas.renderer needed below

    # get the center position for all plots
    top = a[0].get_position().y1
    bottom = a[-1].get_position().y0

    # get the coordinates of the left side of the tick labels 
    x0 = 1
    for at in a:
        at.set_ylabel('') # just to make sure we don't and up with multiple labels
        bboxes, _ = at.yaxis.get_ticklabel_extents(f.canvas.renderer)
        bboxes = bboxes.inverse_transformed(f.transFigure)
        xt = bboxes.x0
        if xt < x0:
            x0 = xt
    tick_label_left = x0

    # set position of label
    a[-1].set_ylabel(ylabel)
    a[-1].yaxis.set_label_coords(tick_label_left - labelpad,(bottom + top)/2, transform=f.transFigure)

length = 100
x = np.linspace(0,100, length)
y1 = np.random.random(length) * 1000
y2 = np.random.random(length)

f,a = plt.subplots(2, sharex=True, gridspec_kw={'hspace':0})
a[0].plot(x, y1)
a[1].plot(x, y2)
set_shared_ylabel(a, 'shared y label (a. u.)')

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

person Hagne    schedule 10.05.2016

Методы в других ответах не будут работать должным образом, если yticks большие. Ярлык будет либо перекрываться метками, либо обрезаться слева, либо полностью невидим / за пределами рисунка.

Я изменил ответ Хагна, чтобы он работал с более чем 1 столбцом подзаголовков как для xlabel, так и для ylabel, и сдвигает график, чтобы ylabel оставался видимым на рисунке.

def set_shared_ylabel(a, xlabel, ylabel, labelpad = 0.01, figleftpad=0.05):
    """Set a y label shared by multiple axes
    Parameters
    ----------
    a: list of axes
    ylabel: string
    labelpad: float
        Sets the padding between ticklabels and axis label"""

    f = a[0,0].get_figure()
    f.canvas.draw() #sets f.canvas.renderer needed below

    # get the center position for all plots
    top = a[0,0].get_position().y1
    bottom = a[-1,-1].get_position().y0

    # get the coordinates of the left side of the tick labels
    x0 = 1
    x1 = 1
    for at_row in a:
        at = at_row[0]
        at.set_ylabel('') # just to make sure we don't and up with multiple labels
        bboxes, _ = at.yaxis.get_ticklabel_extents(f.canvas.renderer)
        bboxes = bboxes.inverse_transformed(f.transFigure)
        xt = bboxes.x0
        if xt < x0:
            x0 = xt
            x1 = bboxes.x1
    tick_label_left = x0

    # shrink plot on left to prevent ylabel clipping
    # (x1 - tick_label_left) is the x coordinate of right end of tick label,
    # basically how much padding is needed to fit tick labels in the figure
    # figleftpad is additional padding to fit the ylabel
    plt.subplots_adjust(left=(x1 - tick_label_left) + figleftpad)

    # set position of label, 
    # note that (figleftpad-labelpad) refers to the middle of the ylabel
    a[-1,-1].set_ylabel(ylabel)
    a[-1,-1].yaxis.set_label_coords(figleftpad-labelpad,(bottom + top)/2, transform=f.transFigure)

    # set xlabel
    y0 = 1
    for at in axes[-1]:
        at.set_xlabel('')  # just to make sure we don't and up with multiple labels
        bboxes, _ = at.xaxis.get_ticklabel_extents(fig.canvas.renderer)
        bboxes = bboxes.inverse_transformed(fig.transFigure)
        yt = bboxes.y0
        if yt < y0:
            y0 = yt
    tick_label_bottom = y0

    axes[-1, -1].set_xlabel(xlabel)
    axes[-1, -1].xaxis.set_label_coords((left + right) / 2, tick_label_bottom - labelpad, transform=fig.transFigure)

Это работает для следующего примера, в то время как ответ Хагне не будет рисовать ylabel (поскольку он находится вне холста), а ylabel KYC перекрывается с метками галочки:

import matplotlib.pyplot as plt
import itertools

fig, axes = plt.subplots(3, 4, sharey='row', sharex=True, squeeze=False)
fig.subplots_adjust(hspace=.5)
for i, a in enumerate(itertools.chain(*axes)):
    a.plot([0,4**i], [0,4**i])
    a.set_title(i)
set_shared_ylabel(axes, 'common X', 'common Y')
plt.show()

В качестве альтернативы, если вас устраивает бесцветная ось, я изменил решение Джулиана Чена, чтобы ylabel не перекрывался с метками галочки.

По сути, нам просто нужно установить ylims для бесцветных, чтобы они соответствовали самым большим ylims подзаголовков, чтобы метки с бесцветными галочками устанавливали правильное местоположение для ylabel.

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

import matplotlib.pyplot as plt
import itertools

fig, axes = plt.subplots(3, 4, sharey='row', sharex=True, squeeze=False)
fig.subplots_adjust(hspace=.5)
miny = maxy = 0
for i, a in enumerate(itertools.chain(*axes)):
    a.plot([0,4**i], [0,4**i])
    a.set_title(i)
    miny = min(miny, a.get_ylim()[0])
    maxy = max(maxy, a.get_ylim()[1])

# add a big axes, hide frame
# set ylim to match the largest range of any subplot
ax_invis = fig.add_subplot(111, frameon=False)
ax_invis.set_ylim([miny, maxy])

# 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")

# shrink plot to prevent clipping
plt.subplots_adjust(left=0.15)
plt.show()
person Tim    schedule 12.06.2019

New in matplotlib 3.4.0

Теперь мы можем использовать supxlabel и supylabel установить общие xlabel и ylabel.

  • Обратите внимание, что это методы FigureBase, поэтому их можно использовать с помощью Figure и _ 7_.

  • Чтобы воспроизвести графики loglog OP (общие метки, но отдельные заголовки):

    x = np.arange(0.01, 10.01, 0.01)
    y = 2 ** x
    
    fig, (ax1, ax2) = plt.subplots(2, 1)
    ax1.loglog(y, x)
    ax2.loglog(x, y)
    
    # subplot titles
    ax1.set_title('ax1.title')
    ax2.set_title('ax2.title')
    
    # common labels
    fig.supxlabel('fig.supxlabel')
    fig.supylabel('fig.supylabel')
    
    plt.tight_layout()
    

    suplabel demo

person tdy    schedule 23.07.2021