pyplot memberikan label untuk subplot

187

Saya punya plot berikut:

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

Saya ingin dapat membuat label sumbu dan judul tidak hanya untuk masing-masing dari dua subplot, tetapi juga label umum yang menjangkau kedua subplot. Misalnya, karena kedua plot memiliki sumbu identik, saya hanya perlu satu set label x dan sumbu y. Saya ingin judul yang berbeda untuk setiap subplot.

Saya mencoba beberapa hal tetapi tidak ada yang berhasil

farqwag25
sumber

Jawaban:

261

Anda dapat membuat subplot besar yang mencakup dua subplot dan kemudian mengatur label umum.

import random
import matplotlib.pyplot as plt

x = range(1, 101)
y1 = [random.randint(1, 100) for _ in xrange(len(x))]
y2 = [random.randint(1, 100) for _ in xrange(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

Cara lain menggunakan fig.text () untuk mengatur lokasi label umum secara langsung.

import random
import matplotlib.pyplot as plt

x = range(1, 101)
y1 = [random.randint(1, 100) for _ in xrange(len(x))]
y2 = [random.randint(1, 100) for _ in xrange(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

Wen-Wei Liao
sumber
1
Fungsi suptitle menggunakan versi fig.text (). Jadi ini mungkin cara "resmi" untuk melakukannya?
PhML
4
Perlu ditekankan bahwa axharus dibuat sebelum ax1dan ax2, jika tidak, plot besar akan menutupi plot kecil.
1 ''
ax.grid (False) atau plt.grid (False) juga diperlukan jika parameter plot global menyertakan kisi (terlihat).
Næreen
3
Tampaknya pendekatan pertama tidak berfungsi lagi dengan versi terbaru dari matplotplib (saya menggunakan 2.0.2): label yang ditambahkan ke kap yang melampirkan tidak terlihat.
M. Toya
Bagaimana cara menambahkan y_labels ke setiap subplot individual?
Fardin
115

Satu cara sederhana menggunakan 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='off', bottom='off', left='off', right='off')
plt.grid(False)
plt.xlabel("common X")
plt.ylabel("common Y")
Julian Chen
sumber
1
ax.grid (False) atau plt.grid (False) juga diperlukan jika parameter plot global menyertakan kisi (terlihat).
Næreen
1
Saya melakukan ini untuk subplot (5, 1) dan ylabel saya berada jauh di tepi kiri jendela daripada di dekat subplot.
Evidlo
1
Anda mendapat dukungan. tapi tolong selalu jelaskan apa yang dilakukan kode, lampirkan gambar atau tunjukkan contoh, karena pasti butuh sedikit waktu untuk mendapatkannya.
Kareem Jeiroudi
4
Ubah 'off'ke Falsedengan versi yang lebih baru dari Matplotlib (Saya punya 2.2.2)
Ted
2
Lalu bagaimana Anda menambahkan plot? for ax in axes: ax.plot(x, y)sepertinya tidak ada gunanya.
usernumber
16

Jawaban Wen-wei Liao baik jika Anda tidak mencoba untuk mengekspor grafis vektor atau bahwa Anda telah mengatur backend matplotlib Anda untuk mengabaikan sumbu berwarna; jika tidak, sumbu tersembunyi akan muncul dalam grafik yang diekspor.

Jawaban saya di suplabelsini mirip dengan fig.suptitleyang menggunakan fig.textfungsi. Oleh karena itu tidak ada artis kapak yang dibuat dan dibuat tidak berwarna. Namun, jika Anda mencoba menyebutnya beberapa kali, Anda akan mendapatkan teks yang ditambahkan di atas satu sama lain (seperti fig.suptitlehalnya juga). Jawaban Wen-wei Liao tidak, karena fig.add_subplot(111)akan mengembalikan objek Axes yang sama jika sudah dibuat.

Fungsi saya juga bisa dipanggil setelah plot dibuat.

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)
KYC
sumber
Ini jawaban imo terbaik. Mudah diimplementasikan dan label tidak tumpang tindih karena opsi labelpad.
Arthur Dent
8

Berikut adalah solusi di mana Anda mengatur ylabel dari salah satu plot dan menyesuaikan posisi sehingga terpusat secara vertikal. Dengan cara ini Anda menghindari masalah yang disebutkan oleh 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.)')

masukkan deskripsi gambar di sini

Hagne
sumber
7

plt.setp() akan melakukan pekerjaan:

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

masukkan deskripsi gambar di sini

MohammadReza
sumber
Apakah ada cara untuk juga mengatur ukuran font / berat dengan metode ini?
pfabri
3
# 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')
J.Zhao
sumber
1

Metode dalam jawaban lain tidak akan berfungsi dengan baik ketika yticks berukuran besar. Ylabel akan tumpang tindih dengan kutu, terpotong di sebelah kiri atau benar-benar tidak terlihat / di luar gambar.

Saya telah memodifikasi jawaban Hagne sehingga berfungsi dengan lebih dari 1 kolom subplot, untuk xlabel dan ylabel, dan ini menggeser plot untuk menjaga agar ylabel tetap terlihat dalam gambar.

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)

Ini berfungsi untuk contoh berikut, sementara jawaban Hagne tidak akan menarik ylabel (karena di luar kanvas) dan ylabel KYC tumpang tindih dengan label centang:

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

Atau, jika Anda baik-baik saja dengan sumbu tidak berwarna, saya telah memodifikasi solusi Julian Chen sehingga ylabel tidak akan tumpang tindih dengan label centang.

Pada dasarnya, kita hanya perlu mengatur ylims dari yang tidak berwarna sehingga cocok dengan ylims terbesar dari subplot sehingga label centang yang tidak berwarna menentukan lokasi yang benar untuk ylabel.

Sekali lagi, kita harus memperkecil plot untuk mencegah kliping. Di sini saya telah mengkodekan jumlah yang menyusut, tetapi Anda dapat bermain-main untuk menemukan nomor yang cocok untuk Anda atau menghitungnya seperti pada metode di atas.

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