Xlabel / ylabel umum untuk subplot matplotlib

143

Saya punya plot berikut:

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

dan sekarang saya ingin memberikan plot ini label x-axis umum dan label y-axis. Dengan "umum", maksud saya harus ada satu label sumbu x besar di bawah seluruh grid subplot, dan satu label sumbu y besar di sebelah kanan. Saya tidak dapat menemukan apa pun tentang ini di dokumentasi untukplt.subplots , dan para googling saya menyarankan saya plt.subplot(111)untuk memulai dengan yang besar - tetapi bagaimana saya kemudian menempatkan subplot 5 * 2 ke dalam penggunaan itu plt.subplots?

Jolindbe
sumber
2
Dengan pembaruan pada pertanyaan, dan komentar yang tersisa di jawaban di bawah ini adalah duplikat dari stackoverflow.com/questions/6963035/…
doyan
Tidak juga, karena pertanyaan saya adalah untuk plt.subplots (), dan pertanyaan yang Anda tautkan menggunakan add_subplot - Saya tidak dapat menggunakan metode itu kecuali saya beralih ke add_subplot, yang ingin saya hindari. Saya bisa menggunakan solusi plt.text yang diberikan sebagai solusi alternatif di tautan Anda, tetapi itu bukan solusi yang paling elegan.
jolindbe
Untuk menguraikan, sejauh yang saya mengerti, plt.subplots tidak dapat menghasilkan satu set subplot dalam lingkungan sumbu yang ada, tetapi selalu membuat angka baru. Baik?
jolindbe
Solusi paling elegan dapat ditemukan di sini: stackoverflow.com/questions/6963035/...
Mr.H
Tautan Anda disediakan oleh pengguna yang terhubung lebih dari 4 tahun yang lalu (hanya beberapa komentar di atas Anda). Seperti yang saya katakan sebelumnya, solusi itu berkaitan dengan add_subplot, dan bukan plt.subplots ().
jolindbe

Jawaban:

206

Ini seperti apa yang sebenarnya Anda inginkan. Ini menerapkan pendekatan yang sama dari jawaban ini untuk kasus spesifik Anda:

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

Banyak plot dengan label sumbu umum

divenex
sumber
4
perhatikan bahwa 0,5 untuk koordinat x dari label-x tidak menempatkan label di tengah subplot tengah. Anda harus sedikit lebih besar dari itu untuk memperhitungkan yticklabels.
dbliss
2
Lihatlah jawaban ini untuk metode yang tidak digunakan plt.text. Anda membuat subplot, tetapi kemudian menambahkan satu plot bit, membuatnya tidak terlihat, dan memberi label x dan y.
James Owers
Terima kasih, bekerja secara umum. Ada solusi untuk kerusakan saat menggunakan tight_layout?
serv-inc
3
@ serv-inc dengan tight_layoutmengganti 0.04dengan 0tampaknya berfungsi.
divenex
3
Menggunakan fig.textbukanlah ide yang bagus. Ini mengacaukan hal-hal sepertiplt.tight_layout()
Damai
55

Karena saya menganggapnya cukup relevan dan elegan (tidak perlu menentukan koordinat untuk menempatkan teks), saya menyalin (dengan sedikit adaptasi) jawaban untuk pertanyaan terkait lainnya .

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

Ini menghasilkan sebagai berikut (dengan matplotlib versi 2.2.0):

5 baris dan 2 kolom subplot dengan label sumbu x dan y yang umum

bli
sumber
4
Karena kesederhanaan ini harus menjadi jawaban yang diterima. Sangat mudah. Masih relevan untuk matplotlib v3.x.
Kyle Swanson
Saya ingin tahu bagaimana ini bisa digunakan dengan beberapa objek gambar? fig.xlabel ("foo") tidak berfungsi.
Horror Vacui
FYI: Sekarang orang menggunakan tema gelap di StackOverflow, labelnya hampir tidak bisa dibaca jadi lebih baik untuk mengekspor png Anda dengan latar belakang putih
xyzzyqed
@xyzzyqed Saya tidak tahu ada yang namanya "tema" di stackoverflow, dan saya bahkan tidak ingat bagaimana saya mengekspornya. Bagaimana saya bisa mengendalikan latar belakang saat mengekspor?
bli
2
Satu-satunya masalah dari solusi ini adalah tidak berfungsi saat digunakan constrained_layout=Truekarena membuat label yang tumpang tindih. Dalam hal ini Anda harus menyesuaikan batas subplot secara manual.
baccandr
35

Tanpa sharex=True, sharey=TrueAnda dapatkan:

masukkan deskripsi gambar di sini

Dengan itu Anda harus mendapatkannya lebih baik:

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

masukkan deskripsi gambar di sini

Tetapi jika Anda ingin menambahkan label tambahan, Anda harus menambahkannya hanya ke plot tepi:

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

masukkan deskripsi gambar di sini

Menambahkan label untuk setiap plot akan merusaknya (mungkin ada cara untuk secara otomatis mendeteksi label berulang, tapi saya tidak menyadarinya).

Piotr Migdal
sumber
Ini jauh lebih sulit jika, misalnya, jumlah plot tidak diketahui (misalnya Anda punya fungsi generalisasi plot yang berfungsi untuk sejumlah subplot).
nucky101
15

Karena perintah:

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

Anda menggunakan mengembalikan tuple yang terdiri dari gambar dan daftar contoh sumbu, itu sudah cukup untuk melakukan sesuatu seperti (pikiran bahwa saya telah berubah fig,axmenjadi 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')

Jika Anda ingin mengubah beberapa rincian pada subplot tertentu, Anda dapat mengaksesnya melalui axes[i]tempat iiterates atas subplot Anda.

Mungkin juga sangat membantu untuk memasukkan a

fig.tight_layout()

di akhir file, sebelum plt.show(), untuk menghindari label yang tumpang tindih.

Marius
sumber
5
Maaf saya agak tidak jelas di atas. Dengan "umum" yang saya maksud adalah satu label x tunggal di bawah semua plot, dan satu label y tunggal di sebelah kiri plot, saya telah memperbarui pertanyaan untuk mencerminkan hal ini.
jolindbe
2
@JohanLindberg: Mengenai komentar Anda di sini dan di atas: Memang plt.subplots()akan membuat contoh sosok baru. Jika Anda ingin tetap dengan perintah ini, Anda dapat dengan mudah menambahkan big_ax = fig.add_subplot(111), karena Anda sudah memiliki angka dan dapat menambahkan sumbu lain. Setelah itu, Anda dapat memanipulasi big_axcara itu ditampilkan di tautan dari Hooked.
Marius
Terima kasih atas saran Anda, tetapi jika saya melakukannya, saya harus menambahkan big_ax setelah plt.subplots (), dan saya mendapatkan subplot itu di atas segalanya - bisakah saya membuatnya transparan atau mengirimnya ke belakang entah bagaimana? Bahkan jika saya mengatur semua warna menjadi tidak ada seperti di tautan Hooked, itu masih berupa kotak putih yang menutupi semua subplot saya.
jolindbe
2
@JohanLindberg, Anda benar, saya belum memeriksanya. Tetapi Anda dapat dengan mudah mengatur warna latar belakang sumbu besar nonedengan melakukan: big_ax.set_axis_bgcolor('none')Anda juga harus membuat warna label none(sebagai lawan dari contoh yang ditautkan oleh Hooked):big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
Marius
2
Saya mendapatkan kesalahan: AttributeError: 'numpy.ndarray' object has no attribute 'set_xlabel'dalam pernyataan ax.set_xlabel('Common x-label'). Bisakah Anda mencari tahu?
hengxin
5

Ini akan terlihat lebih baik jika Anda menyediakan ruang untuk label umum dengan membuat label yang tidak terlihat untuk subplot di sudut kiri bawah. Juga bagus untuk mengirimkan fontsize dari rcParams. Dengan cara ini, label umum akan berubah ukuran dengan pengaturan rc Anda, dan sumbu juga akan disesuaikan untuk memberikan ruang bagi label umum.

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

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

EL_DON
sumber
1
Penggunaan label tak terlihat yang bagus! Terima kasih
colelemonz
3

Saya mengalami masalah yang sama saat merencanakan kotak grafik. Grafik terdiri dari dua bagian (atas dan bawah). Label-y seharusnya dipusatkan di kedua bagian.

Saya tidak ingin menggunakan solusi yang tergantung pada mengetahui posisi pada gambar luar (seperti fig.text ()), jadi saya memanipulasi posisi y dari fungsi set_ylabel (). Biasanya 0,5, di tengah plot ditambahkan. Karena padding antara bagian-bagian (hspace) dalam kode saya adalah nol, saya bisa menghitung bagian tengah dari dua bagian relatif ke bagian atas.

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

gambar

Kerugian:

  • Jarak horizontal ke plot didasarkan pada bagian atas, kutu bawah mungkin meluas ke label.

  • Formula tidak memperhitungkan ruang antar bagian.

  • Melempar pengecualian ketika ketinggian bagian atas adalah 0.

Mungkin ada solusi umum yang mempertimbangkan antara angka-angka.

CPe
sumber
Hei, saya menemukan cara untuk melakukan ini sangat banyak dalam jawaban Anda, tetapi mungkin memecahkan beberapa masalah ini; lihat stackoverflow.com/a/44020303/4970632 (di bawah)
Luke Davis
2

Memperbarui:

Fitur ini sekarang merupakan bagian dari paket proplot matplotlib yang baru-baru ini saya rilis di pypi. Secara default, saat Anda membuat angka, label "dibagi" di antara sumbu.


Jawaban asli:

Saya menemukan metode yang lebih kuat:

Jika Anda tahu bottomdan topkwarg yang masuk ke GridSpecinisialisasi, atau Anda tahu posisi tepi sumbu Anda dalam Figurekoordinat , Anda juga dapat menentukan posisi ylabel dalam Figurekoordinat dengan sihir "transformasi" yang bagus. Sebagai contoh:

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

... dan Anda akan melihat bahwa label masih dengan tepat menyesuaikan kiri-kanan agar tidak tumpang tindih dengan label ticklabel, seperti biasa - tetapi sekarang akan menyesuaikan untuk selalu tepat di antara subplot yang diinginkan.

Selain itu, jika Anda bahkan tidak menggunakan set_position, ylabel akan muncul secara default tepat di tengah angka . Saya menduga ini karena ketika label akhirnya ditarik, matplotlibmenggunakan 0,5 untuk- ykoordinat tanpa memeriksa apakah transformasi koordinat yang mendasarinya telah berubah.

Luke Davis
sumber