Matplotlib tight_layout () ไม่ได้คำนึงถึงตัวเลขบัญชี


208

ถ้าฉันเพิ่มคำบรรยายลงในรูป matplotlib ของฉันมันจะถูกทับด้วยหัวเรื่องของแผนการย่อย ไม่มีใครรู้วิธีการดูแลที่ง่าย? ฉันพยายามtight_layout()ฟังก์ชั่น แต่มันยิ่งทำให้เรื่องแย่ลงเท่านั้น

ตัวอย่าง:

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

คำตอบ:


179

คุณสามารถปรับรูปแบบย่อยทางเรขาคณิตในการtight_layoutโทรมากดังต่อไปนี้:

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

ตามที่ระบุไว้ในเอกสาร ( https://matplotlib.org/users/tight_layout_guide.html ):

tight_layout()พิจารณาเฉพาะ ticklabels ป้ายกำกับแกนและชื่อ ดังนั้นศิลปินอื่น ๆ อาจถูกตัดและอาจทับซ้อนกัน


1
มันทำงานได้อย่างมีเสน่ห์ แต่การเปลี่ยนแปลงเกิดขึ้นเมื่อระบุกล่องขอบได้อย่างไร ฉันอ่านเอกสาร แต่ไม่ค่อยชัดเจนสำหรับฉัน มันจะดีถ้าคุณสามารถอธิบายให้ฉัน! ขอบคุณ
thepunitsingh

2
คุณช่วยอธิบายความหมายของตัวเลขแต่ละตัวใน rect ได้ไหม? ฉันไม่เห็นพวกเขาในเอกสาร
steven

6
@steven ดูtight_layoutเอกสาร:[left, bottom, right, top] in normalized (0, 1) figure coordinates
soupault

1
คุณสามารถเพิ่มช่องว่างระหว่าง suptitle และ axes ได้มากขึ้นโดยการลดค่าที่ถูกต้อง: tight_layout(rect=[0, 0.03, 1, 0.9])แทนที่จะ0.95เป็นในคำตอบ
ComFreek

1
ไม่ทำงานภายในจูปีเตอร์ พยายามใช้ค่าที่แตกต่างกันสำหรับตัวเลขไม่มีผล
Aleksejs Fomins

114

คุณสามารถปรับระยะห่างได้ด้วยตนเองโดยใช้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()

5
โปรดสังเกตว่าค่าเริ่มต้นสำหรับ top คือ 0.9 เมื่อคุณโทรsubplots_adjust
Brian Burns

72
ฉันไม่สามารถเชื่อในปี 2560 ของเจ้าของเรานี่ยังคงเป็นข้อผิดพลาดของ matplotlib
wordoforthewise

29
โปรดทราบว่าsubplots_adjustจะต้องเรียกว่าหลังจากที่สายไปหรือจะไม่มีผลกระทบของการโทรtight_layout subplots_adjust
Achal Dave

7
@ คำหลักถัดไปทำให้เป็นจริงได้ในปี 2018
vlsd

6
@vlsd สวัสดีจาก 2019
เซี่ยวหยูลู

55

สิ่งหนึ่งที่คุณสามารถเปลี่ยนรหัสได้อย่างง่ายดายคือfontsizeคุณใช้ชื่อเรื่อง อย่างไรก็ตามฉันจะสมมติว่าคุณไม่ต้องการทำอย่างนั้น!

ทางเลือกในการใช้fig.subplots_adjust(top=0.85):

โดยปกติแล้วจะtight_layout()ทำได้ค่อนข้างดีในการวางตำแหน่งทุกอย่างในสถานที่ที่ดีเพื่อให้พวกเขาไม่ทับซ้อนกัน เหตุผลtight_layout()ไม่ได้ช่วยในกรณีนี้เพราะtight_layout()ไม่ได้คำนึงถึง fig.suptitle () มีปัญหาแบบเปิดเกี่ยวกับเรื่องนี้ใน GitHub: https://github.com/matplotlib/matplotlib/issues/829 [ปิดในปี 2014 เนื่องจากต้องใช้ผู้จัดการรูปเรขาคณิตเต็ม - เปลี่ยนเป็นhttps://github.com/matplotlib/matplotlib / ประเด็น / 1109 ]

GridSpecถ้าคุณอ่านกระทู้ที่มีวิธีการแก้ปัญหาของคุณที่เกี่ยวข้องกับ กุญแจสำคัญคือการเว้นช่องว่างที่ด้านบนของรูปเมื่อโทรtight_layoutโดยใช้rectkwarg สำหรับปัญหาของคุณรหัสกลายเป็น:

ใช้ 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()

ผลลัพธ์:

ใช้ gridspec

อาจGridSpecเป็นเรื่องที่เกินความจำเป็นเล็กน้อยสำหรับคุณหรือปัญหาที่แท้จริงของคุณจะเกี่ยวข้องกับแผนการย่อยอื่น ๆ อีกมากมายบนผืนผ้าใบที่มีขนาดใหญ่กว่าหรือมีภาวะแทรกซ้อนอื่น ๆ สับง่ายๆคือการใช้งานเพียงannotate()และล็อคพิกัดกับการเลียนแบบ'figure fraction' suptitleคุณอาจต้องทำการปรับเปลี่ยนบางอย่างให้ละเอียดยิ่งขึ้นเมื่อคุณดูที่เอาต์พุต โปรดทราบว่าโซลูชันที่สองนี้ไม่ได้ใช้tight_layout()ใช้งาน

วิธีแก้ปัญหาที่ง่ายกว่า (แต่อาจต้องปรับให้ละเอียด)

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

ผลลัพธ์:

ง่าย

[ใช้Python2.7.3 (64- บิต) และmatplotlib1.2.0]


1
ขอบคุณถ้าฉันใช้วิธีแก้ปัญหาแรกที่คุณแนะนำ (โดยใช้GridSpec) ฉันจะสร้างแผนการย่อยที่ใช้แกนร่วมกันได้อย่างไร ฉันมักจะใช้plt.subplots(N,1, sharex=<>, sharey=<>)เมื่อสร้าง subplots แต่รหัสที่คุณโพสต์ใช้add_subplotแทน
Amelio Vazquez-Reina

10
การหล่อfig.tight_layout(rect=[0, 0.03, 1, 0.95])ยังใช้ได้
soupault

1
ทางออกที่สองเป็นทางเดียวที่ได้ผลสำหรับฉัน ขอบคุณ!
Hagbard

19

ทางเลือกที่ง่ายและใช้งานง่ายคือการปรับพิกัดของข้อความ suptitle ในรูปโดยใช้อาร์กิวเมนต์ y ในการเรียกใช้ suptitle (ดูเอกสาร ):

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

8
นี่เป็นวิธีที่ยอดเยี่ยมในโน้ตบุ๊ก แต่คำสั่ง plt.savefig () ไม่เชื่อฟัง โซลูชันนี้ยังคงตัดหัวเรื่องในคำสั่ง savefig
ซูเปอร์ฮีโร่

11

รูปแบบที่แน่นไม่ทำงานกับ suptitle แต่constrained_layoutทำได้ ดูคำถามนี้ปรับปรุงขนาด / ระยะห่างของพล็อตย่อยด้วยแผนการย่อยจำนวนมากใน matplotlib

ฉันพบการเพิ่มแผนการย่อยในครั้งเดียวดูดีขึ้นเช่น

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

# then iterating over the axes to fill in the plots

แต่มันยังสามารถเพิ่มที่จุดที่ร่างถูกสร้างขึ้น:

fig = plt.figure(constrained_layout=True)

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

หมายเหตุ: เพื่อให้แผนย่อยของฉันเข้าใกล้กันมากขึ้นฉันก็ใช้เช่นกัน

fig.subplots_adjust(wspace=0.05)

และ constrained_layout ไม่ทำงานกับสิ่งนี้ :(


4

ผมได้ต่อสู้กับ matplotlib ตัดวิธีดังนั้นฉันได้ตอนนี้เพิ่งทำฟังก์ชั่นการทำเช่นนี้ผ่านทางที่bashเรียกร้องให้ImageMagick's คำสั่ง mogrifyซึ่งทำงานได้ดีและได้รับพื้นที่สีขาวทั้งหมดพิเศษปิดขอบร่าง สิ่งนี้ต้องการให้คุณใช้ UNIX / Linux กำลังใช้bashเชลล์และImageMagickติดตั้งแล้ว

เพียงแค่โทรไปที่นี้หลังจากที่คุณsavefig()โทร

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)

3

ตามที่กล่าวไว้โดยคนอื่นโดยค่าเริ่มต้นเค้าโครงแน่นไม่คำนึงถึงการบัญชี อย่างไรก็ตามฉันพบว่ามีความเป็นไปได้ที่จะใช้bbox_extra_artistsอาร์กิวเมนต์เพื่อส่งผ่านใน suptitle เป็นกล่อง bounding ที่ควรนำมาพิจารณา:

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

สิ่งนี้บังคับให้มีการคำนวณเลย์เอาต์อย่างแน่นหนาเพื่อsuptitleพิจารณาและดูเหมือนว่าคุณจะคาดหวัง


นี่เป็นตัวเลือกเดียวที่เหมาะกับฉัน ฉันใช้สมุดบันทึก Jupyter เพื่อสร้างตัวเลขและบันทึก ฉันทำงานใน python 3.6 บนเครื่อง Linux
GRquanti

2

สิ่งเดียวที่ใช้ได้ผลสำหรับฉันคือการแก้ไขการโทรไปยัง suptitle:

fig.suptitle("title", y=.995)

1

ฉันมีปัญหาคล้ายกันที่ครอบตัดเมื่อใช้tight_layoutสำหรับตารางที่มีขนาดใหญ่มาก (มากกว่า 200 subplots) และเรนเดอร์ในสมุดบันทึก jupyter ฉันได้แก้ปัญหาอย่างรวดเร็วที่มักทำให้คุณsuptitleอยู่ในระยะที่ไกลกว่าแผนย่อยหลักของคุณ:

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)

สำหรับพล็อตย่อยที่มีขนาดต่างกันคุณยังคงสามารถใช้วิธีนี้เพื่อรับค่าสูงสุดของแผนย่อยสูงสุดจากนั้นกำหนดจำนวนเพิ่มเติมด้วยตนเองเพื่อเพิ่มลงใน suptitle

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.