programing

하위 그림의 공통 축 레이블 설정 방법

muds 2023. 6. 25. 20:36
반응형

하위 그림의 공통 축 레이블 설정 방법

저는 다음과 같은 줄거리를 가지고 있습니다.

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 축 레이블 세트가 하나만 필요합니다.하위 플롯마다 다른 제목을 원합니다.

몇 가지를 시도해 보았지만 제대로 작동하지 않았습니다.

두 개의 하위 그림을 포함하는 큰 하위 그림을 만든 다음 공통 레이블을 설정할 수 있습니다.

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

한 가지 간단한 사용 방법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")

새로운 정보 표시 lib 3.4.0

이제 공통 축 레이블을 설정하는 기본 제공 방법이 있습니다.


작업을 재현하는 방법loglog그림(공통 레이블이지만 별도의 제목):

x = np.arange(0.01, 10.01, 0.01)
y = 2 ** x

fig, (ax1, ax2) = plt.subplots(2, 1, constrained_layout=True)
ax1.loglog(y, x)
ax2.loglog(x, y)

# separate subplot titles
ax1.set_title('ax1.title')
ax2.set_title('ax2.title')

# common axis labels
fig.supxlabel('fig.supxlabel')
fig.supylabel('fig.supylabel')

suplabel demo

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

enter image description here

벡터 그래픽을 내보내려고 하지 않거나 색이 없는 축을 무시하도록 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)

다음 솔루션에서는 그림 중 하나의 y 레이블을 설정하고 그래프의 위치를 수직으로 중앙에 오도록 조정할 수 있습니다.이렇게 하면 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.)')

enter image description here

# list loss and acc are your data
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)

ax1.plot(iteration1, loss)
ax2.plot(iteration2, acc)

ax1.set_title('Training Loss')
ax2.set_title('Training Accuracy')

ax1.set_xlabel('Iteration')
ax1.set_ylabel('Loss')

ax2.set_xlabel('Iteration')
ax2.set_ylabel('Accuracy')

yticks가 크면 다른 답변의 메서드가 제대로 작동하지 않습니다.y 레이블은 눈금과 겹치거나 왼쪽에서 잘리거나 그림 밖에서 완전히 보이지 않거나 표시되지 않습니다.

Hagne의 답변을 수정하여 xlabel과 ylabel 모두에 대해 1개 이상의 하위 플롯 열에서 작동하고 그림에서 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)

Hagne의 답변은 캔버스 밖에 있으므로 drawy label을 그릴 수 없으며 KYC의 y label은 눈금 레이블과 겹칩니다.

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

또는 무채색 축에 문제가 없다면 Julian Chen의 솔루션을 수정하여 눈금 레이블과 겹치지 않도록 하였습니다.

기본적으로 무색의 임을 설정하여 하위 플롯의 가장 큰 임과 일치하도록 하면 무색의 눈금 레이블이 y 레이블의 정확한 위치를 설정할 수 있습니다.

다시 말하지만, 우리는 클리핑을 방지하기 위해 플롯을 축소해야 합니다.여기서 축소할 양을 하드 코딩했지만, 여러분은 자신에게 맞는 숫자를 찾거나 위의 방법처럼 계산할 수 있습니다.

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

다음과 같이 축에 "set"를 사용할 수 있습니다.

axes[0].set(xlabel="KartalOl", ylabel="Labeled")

언급URL : https://stackoverflow.com/questions/6963035/how-to-set-common-axes-labels-for-subplots

반응형