Matplotlib chặt_layout () không tính đến suptitle hình


207

Nếu tôi thêm phụ đề vào hình matplotlib của mình, nó sẽ bị che lấp bởi các tiêu đề của subplot. Có ai biết làm thế nào để dễ dàng chăm sóc điều đó? Tôi đã thử tight_layout()chức năng, nhưng nó chỉ làm cho mọi thứ tồi tệ hơn.

Thí dụ:

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

Câu trả lời:


178

Bạn có thể điều chỉnh hình dạng subplot trong chính tight_layoutcuộc gọi như sau:

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

Như đã nêu trong tài liệu ( https://matplotlib.org/users/tight_layout_guide.html ):

tight_layout()chỉ xem xét ticklabels, nhãn trục và tiêu đề. Do đó, các nghệ sĩ khác có thể được cắt bớt và cũng có thể chồng chéo.


1
Nó hoạt động như bùa mê, nhưng làm thế nào thay đổi xảy ra khi chỉ định hộp giới hạn? Tôi đọc tài liệu nhưng không rõ ràng nhiều với tôi. Sẽ thật tuyệt nếu bạn có thể vui lòng giải thích cho tôi! Cảm ơn.
theastaitsingh

2
bạn có thể vui lòng giải thích ý nghĩa của từng số trong trực tràng? Tôi đã không nhìn thấy chúng trong tài liệu.
steven

6
@steven xem tight_layouttài liệu:[left, bottom, right, top] in normalized (0, 1) figure coordinates
súpault

1
Bạn có thể tăng khoảng cách giữa phụ đề và trục thậm chí nhiều hơn bằng cách hạ thấp giá trị đúng nhất: tight_layout(rect=[0, 0.03, 1, 0.9])thay vì 0.95như trong câu trả lời.
ComFalet

1
Không làm việc bên trong jupyter. Đã thử các giá trị khác nhau cho các con số, không có hiệu lực
Fseins của Mitchsejs

114

Bạn có thể tự điều chỉnh khoảng cách bằng cách sử dụng 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
Lưu ý giá trị mặc định cho đầu là 0,9 khi bạn gọisubplots_adjust
Brian Burns

72
Tôi không thể tin vào năm 2017 của chúa tể chúng ta, đây vẫn là một lỗi của matplotlib.
lời giới thiệu

29
Lưu ý rằng subplots_adjustphải được gọi sau bất kỳ cuộc gọi nào tight_layout, hoặc sẽ không có hiệu lực của cuộc gọi subplots_adjust.
Achal Dave

7
@wordsforthewise thực hiện năm 2018 ngay bây giờ
vlsd 16/03/18

6
@vlsd Xin chào từ năm 2019
Xiaoyu Lu

55

Một điều bạn có thể thay đổi trong mã của mình rất dễ dàng là fontsizebạn đang sử dụng cho các tiêu đề. Tuy nhiên, tôi sẽ cho rằng bạn không chỉ muốn làm điều đó!

Một số lựa chọn thay thế cho việc sử dụng fig.subplots_adjust(top=0.85):

Thường tight_layout()làm một công việc khá tốt trong việc định vị mọi thứ ở những vị trí tốt để chúng không bị chồng chéo. Lý do tight_layout()không giúp ích trong trường hợp này là vì tight_layout()không tính đến fig.suptitle (). Có một vấn đề mở về vấn đề này trên GitHub: https://github.com/matplotlib/matplotlib/issues/829 [đã đóng cửa vào năm 2014 do yêu cầu người quản lý hình học đầy đủ - chuyển sang https://github.com/matplotlib/matplotlib / vấn đề / 1109 ].

Nếu bạn đọc các chủ đề, có một giải pháp cho vấn đề của bạn liên quan GridSpec. Điều quan trọng là để lại một khoảng trống ở trên cùng của hình khi gọi tight_layout, sử dụngrect kwarg. Đối với vấn đề của bạn, mã trở thành:

Sử dụng 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()

Kết quả:

sử dụng gridspec

Có thể GridSpeclà một chút quá mức cho bạn, hoặc vấn đề thực sự của bạn sẽ liên quan đến nhiều ô phụ hơn trên một khung vẽ lớn hơn nhiều, hoặc các biến chứng khác. Một cách hack đơn giản là chỉ cần sử dụng annotate()và khóa tọa độ 'figure fraction'để bắt chước a suptitle. Bạn có thể cần phải thực hiện một số điều chỉnh tốt hơn một khi bạn nhìn vào đầu ra, mặc dù. Lưu ý rằng giải pháp thứ hai này không sử dụngtight_layout() .

Giải pháp đơn giản hơn (mặc dù có thể cần phải tinh chỉnh)

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

Kết quả:

đơn giản

[Sử dụng Python2.7.3 (64 bit) và matplotlib1.2.0]


1
Cảm ơn, nếu tôi sử dụng giải pháp đầu tiên mà bạn đề xuất (sử dụng GridSpec), làm thế nào tôi có thể tạo các ô con chia sẻ trục? Tôi thường sử dụng plt.subplots(N,1, sharex=<>, sharey=<>)khi tạo các ô con, nhưng mã bạn đã đăng sử dụng add_subplotthay vào đó
Amelio Vazquez-Reina

10
Đúc fig.tight_layout(rect=[0, 0.03, 1, 0.95])cũng hoạt động.
súpault

1
Giải pháp thứ hai là giải pháp duy nhất phù hợp với tôi. Cảm ơn bạn!
Hagbard

19

Một giải pháp thay thế và đơn giản để sử dụng là điều chỉnh tọa độ của văn bản suptitle trong hình bằng cách sử dụng đối số y trong lệnh gọi của suptitle (xem tài liệu ):

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
Đây là một cách tuyệt vời trong một máy tính xách tay nhưng lệnh plt.savefig () không tuân theo. Giải pháp này vẫn cắt bỏ tiêu đề trong lệnh savefig.
siêu anh hùng

11

Bố cục chặt chẽ không hoạt động với suptitle, nhưng constrained_layoutkhông. Xem câu hỏi này Cải thiện kích thước / khoảng cách của subplot với nhiều subplot trong matplotlib

Tôi thấy việc thêm các ô con cùng một lúc trông tốt hơn, tức là

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

# then iterating over the axes to fill in the plots

Nhưng nó cũng có thể được thêm vào tại thời điểm con số được tạo ra:

fig = plt.figure(constrained_layout=True)

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

Lưu ý: Để làm cho các ô con của tôi gần nhau hơn, tôi cũng đang sử dụng

fig.subplots_adjust(wspace=0.05)

và ràng buộc_layout không hoạt động với điều này :(


4

Tôi đã đấu tranh với matplotlib cắt tỉa phương pháp, vì vậy bây giờ tôi đã chỉ cần thực hiện một chức năng để thực hiện điều này thông qua một bashcuộc gọi đến ImageMagickcủa lệnh mogrify , mà hoạt động tốt và được tất cả khoảng trắng thêm ra rìa của hình. Điều này yêu cầu bạn đang sử dụng UNIX / Linux, đang sử dụng bashshell và đã ImageMagickcài đặt.

Chỉ cần thực hiện một cuộc gọi đến đây sau savefig()cuộc gọi của bạn .

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

Như đã đề cập bởi những người khác, theo mặc định, bố cục chặt chẽ không tính đến phụ đề. Tuy nhiên, tôi đã thấy có thể sử dụng bbox_extra_artistsđối số để chuyển trong suptitle như một hộp giới hạn cần được tính đến:

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

Điều này buộc tính toán bố cục chặt chẽ phải tính đến suptitle, và nó trông giống như bạn mong đợi.


Đây là lựa chọn duy nhất làm việc cho tôi. Tôi sử dụng máy tính xách tay Jupyter để tạo số liệu và lưu chúng. Tôi làm việc trong python 3.6 trên máy Linux.
GRquanti

1

Tôi đã gặp một vấn đề tương tự mà bị cắt xén khi sử dụng tight_layoutcho một lưới các ô rất lớn (hơn 200 ô phụ) và hiển thị trong một sổ ghi chép jupyter. Tôi đã thực hiện một giải pháp nhanh chóng luôn đặt bạn suptitleở một khoảng cách nhất định trên ô phụ hàng đầu của bạn:

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)

Đối với các ô phụ có kích thước thay đổi, bạn vẫn có thể sử dụng phương pháp này để lấy đỉnh của ô phụ trên cùng, sau đó xác định thủ công một số tiền bổ sung để thêm vào phụ đề.


1

Điều duy nhất làm việc cho tôi là sửa đổi cuộc gọi thành suptitle:

fig.suptitle("title", y=.995)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.