Matplotlib tight_layout () tidak memperhitungkan suptitle gambar akun

208

Jika saya menambahkan subtitle ke figur matplotlib saya, itu akan ditimpa oleh judul subplot. Adakah yang tahu cara mudah mengatasi itu? Saya mencoba tight_layout()fungsinya, tetapi itu hanya memperburuk keadaan.

Contoh:

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.tight_layout()
plt.show()
katrasnikj
sumber

Jawaban:

180

Anda dapat menyesuaikan geometri subplot dengan tight_layoutpanggilan sebagai berikut:

fig.tight_layout(rect=[0, 0.03, 1, 0.95])

Seperti yang dinyatakan dalam dokumentasi ( https://matplotlib.org/users/tight_layout_guide.html ):

tight_layout()hanya mempertimbangkan label centang, label sumbu, dan judul. Dengan demikian, seniman lain mungkin terpotong dan juga tumpang tindih.

soupault
sumber
1
Itu bekerja seperti pesona, tetapi bagaimana perubahan terjadi ketika menentukan kotak pembatas? Saya membaca dokumen tetapi tidak terlalu jelas bagi saya. Akan sangat bagus jika Anda bisa menjelaskan kepada saya! Terima kasih.
thepunitsingh
2
Bisakah Anda jelaskan arti dari setiap angka dalam kotak? Saya tidak melihat mereka di doc.
steven
6
@steven lihat tight_layoutdokumen:[left, bottom, right, top] in normalized (0, 1) figure coordinates
soupault
1
Anda dapat menambah ruang antara suptitle dan sumbu lebih banyak lagi dengan menurunkan nilai paling benar: tight_layout(rect=[0, 0.03, 1, 0.9])alih-alih 0.95seperti pada jawabannya.
ComFreek
1
Tidak berfungsi di dalam jupyter. Mencoba nilai yang berbeda untuk angka, tidak berpengaruh
Aleksejs Fomins
114

Anda dapat secara manual mengatur jarak menggunakan plt.subplots_adjust(top=0.85):

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.subplots_adjust(top=0.85)
plt.show()
unutbu
sumber
5
Perhatikan nilai default untuk top adalah 0,9 saat Anda meneleponsubplots_adjust
Brian Burns
72
Saya tidak percaya pada tahun 2017 dari tuan kami ini masih merupakan bug dari matplotlib.
kata
29
Catatan yang subplots_adjustharus dipanggil setelah ada panggilan ke tight_layout, atau tidak akan ada efek panggilan subplots_adjust.
Achal Dave
7
@wordscepatnya buat 2018 sekarang
vlsd
6
@vlsd Halo dari 2019
Xiaoyu Lu
55

Satu hal yang dapat Anda ubah dalam kode Anda dengan sangat mudah adalah fontsize Anda menggunakan judul-judulnya. Namun, saya akan berasumsi bahwa Anda tidak hanya ingin melakukan itu!

Beberapa alternatif untuk menggunakan fig.subplots_adjust(top=0.85) :

Biasanya tight_layout()melakukan pekerjaan yang cukup baik dalam memposisikan segala sesuatu di lokasi yang baik sehingga mereka tidak tumpang tindih. Alasannya tight_layout()tidak membantu dalam kasus ini adalah karena tight_layout()tidak memperhitungkan fig.suptitle (). Ada masalah terbuka tentang ini di GitHub: https://github.com/matplotlib/matplotlib/issues/829 [ditutup pada 2014 karena membutuhkan manajer geometri penuh - dialihkan ke https://github.com/matplotlib/matplotlib / issues / 1109 ].

Jika Anda membaca utas, ada solusi untuk masalah Anda yang melibatkan GridSpec. Kuncinya adalah meninggalkan ruang di bagian atas gambar saat memanggil tight_layout, menggunakan rectkwarg. Untuk masalah Anda, kodenya menjadi:

Menggunakan GridSpec

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

f = np.random.random(100)
g = np.random.random(100)

fig = plt.figure(1)
gs1 = gridspec.GridSpec(1, 2)
ax_list = [fig.add_subplot(ss) for ss in gs1]

ax_list[0].plot(f)
ax_list[0].set_title('Very Long Title 1', fontsize=20)

ax_list[1].plot(g)
ax_list[1].set_title('Very Long Title 2', fontsize=20)

fig.suptitle('Long Suptitle', fontsize=24)    

gs1.tight_layout(fig, rect=[0, 0.03, 1, 0.95])  

plt.show()

Hasil:

menggunakan gridspec

Mungkin GridSpecsedikit berlebihan bagi Anda, atau masalah Anda yang sebenarnya akan melibatkan lebih banyak subplot pada kanvas yang jauh lebih besar, atau komplikasi lainnya. Retas sederhana adalah dengan hanya menggunakan annotate()dan mengunci koordinat 'figure fraction'untuk meniru a suptitle. Anda mungkin perlu melakukan beberapa penyesuaian yang lebih baik setelah Anda melihat hasilnya. Perhatikan bahwa solusi kedua ini tidak digunakan tight_layout().

Solusi yang lebih sederhana (meskipun mungkin perlu disesuaikan)

fig = plt.figure(2)

ax1 = plt.subplot(121)
ax1.plot(f)
ax1.set_title('Very Long Title 1', fontsize=20)

ax2 = plt.subplot(122)
ax2.plot(g)
ax2.set_title('Very Long Title 2', fontsize=20)

# fig.suptitle('Long Suptitle', fontsize=24)
# Instead, do a hack by annotating the first axes with the desired 
# string and set the positioning to 'figure fraction'.
fig.get_axes()[0].annotate('Long Suptitle', (0.5, 0.95), 
                            xycoords='figure fraction', ha='center', 
                            fontsize=24
                            )
plt.show()

Hasil:

sederhana

[Menggunakan Python2.7.3 (64-bit) dan matplotlib1.2.0]

aseagram
sumber
1
Terima kasih, jika saya menggunakan solusi pertama yang Anda sarankan (menggunakan GridSpec) bagaimana saya bisa membuat subplot yang berbagi sumbu? Saya biasanya menggunakan plt.subplots(N,1, sharex=<>, sharey=<>)saat membuat subplot, tetapi kode yang Anda posting menggunakan add_subplotsebaliknya
Amelio Vazquez-Reina
10
Casting fig.tight_layout(rect=[0, 0.03, 1, 0.95])juga berhasil.
soupault
1
Solusi kedua adalah satu-satunya yang bekerja untuk saya. Terima kasih!
Hagbard
19

Solusi alternatif dan mudah digunakan adalah menyesuaikan koordinat teks suptitle pada gambar menggunakan argumen y dalam panggilan suptitle (lihat dokumen ):

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', y=1.05, fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.show()
Puggie
sumber
8
Ini adalah cara yang bagus dalam buku catatan tetapi perintah plt.savefig () tidak patuh. Solusi ini masih memotong judul dalam perintah savefig.
superhero
11

Tata letak yang ketat tidak bekerja dengan suptitle, tetapi constrained_layouttidak. Lihat pertanyaan ini Tingkatkan ukuran / jarak subplot dengan banyak subplot di matplotlib

Saya menemukan menambahkan subplot sekaligus terlihat lebih baik, yaitu

fig, axs = plt.subplots(rows, cols, constrained_layout=True)

# then iterating over the axes to fill in the plots

Tapi itu juga bisa ditambahkan pada saat gambar dibuat:

fig = plt.figure(constrained_layout=True)

ax1 = fig.add_subplot(cols, rows, 1)
# etc

Catatan: Untuk membuat subplot saya lebih dekat, saya juga menggunakan

fig.subplots_adjust(wspace=0.05)

dan constrained_layout tidak berfungsi dengan ini :(

bhayley
sumber
4

Saya telah berjuang dengan matplotlib pemangkasan metode, jadi saya sekarang hanya membuat fungsi untuk melakukan hal ini melalui bashpanggilan untuk ImageMagick's perintah mogrify , yang bekerja dengan baik dan mendapat semua spasi ekstra dari tepi sosok ini. Ini mengharuskan Anda menggunakan UNIX / Linux, menggunakan bashshell, dan telah ImageMagickmenginstal.

Hanya melakukan panggilan ke ini setelah savefig()panggilan Anda .

def autocrop_img(filename):
    '''Call ImageMagick mogrify from bash to autocrop image'''
    import subprocess
    import os

    cwd, img_name = os.path.split(filename)

    bashcmd = 'mogrify -trim %s' % img_name
    process = subprocess.Popen(bashcmd.split(), stdout=subprocess.PIPE, cwd=cwd)
ryanjdillon
sumber
3

Seperti yang disebutkan oleh orang lain, secara default tata letak yang ketat tidak memperhitungkan suptitle. Namun, saya telah menemukan bahwa mungkin untuk menggunakan bbox_extra_artistsargumen untuk meneruskan suptitle sebagai kotak pembatas yang harus diperhitungkan:

st = fig.suptitle("My Super Title")
plt.savefig("figure.png", bbox_extra_artists=[st], bbox_inches='tight')

Ini memaksa perhitungan tata letak yang ketat untuk suptitlememperhitungkannya, dan tampaknya seperti yang Anda harapkan.

Herman Schaaf
sumber
Ini adalah satu-satunya opsi yang berfungsi untuk saya. Saya menggunakan notebook Jupyter untuk membuat angka dan menyimpannya. Saya bekerja di python 3.6 pada mesin Linux.
GRquanti
2

Satu-satunya hal yang berhasil bagi saya adalah memodifikasi panggilan ke suptitle:

fig.suptitle("title", y=.995)
markemus
sumber
1

Saya memiliki masalah serupa yang muncul ketika menggunakan tight_layoutuntuk plot plot yang sangat besar (lebih dari 200 subplot) dan rendering di notebook jupyter. Saya membuat solusi cepat yang selalu menempatkan Anda suptitlepada jarak tertentu di atas subplot teratas Anda:

import matplotlib.pyplot as plt

n_rows = 50
n_col = 4
fig, axs = plt.subplots(n_rows, n_cols)

#make plots ...

# define y position of suptitle to be ~20% of a row above the top row
y_title_pos = axs[0][0].get_position().get_points()[1][1]+(1/n_rows)*0.2
fig.suptitle('My Sup Title', y=y_title_pos)

Untuk subplot berukuran bervariasi, Anda masih dapat menggunakan metode ini untuk mendapatkan bagian atas subplot teratas, kemudian secara manual menentukan jumlah tambahan untuk ditambahkan ke judul buku.

Brian Pollack
sumber