Matplotlib tight_layout()没有考虑数字suptitle

时间:2011-11-23 20:12:47

标签: python matplotlib

如果我在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()

11 个答案:

答案 0 :(得分:109)

您可以在tight_layout调用中调整子图几何体,如下所示:

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

正如文档(https://matplotlib.org/users/tight_layout_guide.html)中所述:

  

tight_layout()仅考虑ticklabels,轴标签和标题。因此,其他艺术家可能会被剪裁,也可能会重叠。

P.S。社区建议我发表评论作为答案。

答案 1 :(得分:102)

您可以使用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()

答案 2 :(得分:48)

您可以非常轻松地更改代码中的一件事是您用于标题的fontsize。但是,我会假设你不只是想这样做!

使用fig.subplots_adjust(top=0.85)的一些替代方法:

通常tight_layout()可以很好地将所有内容放在好位置,这样它们就不会重叠。 tight_layout()在这种情况下没有帮助的原因是因为 tight_layout()没有考虑fig.suptitle()。 GitHub上存在一个未解决的问题:{ {3}} [2014年因需要完整的几何管理器而关闭 - 转移到https://github.com/matplotlib/matplotlib/issues/829]。

如果您阅读了该主题,则可以找到涉及GridSpec的问题的解决方案。关键是在使用tight_layout kwarg调用rect时在图的顶部留出一些空格。对于您的问题,代码变为:

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

结果:

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

结果:

simple

[使用Python 2.7.3(64位)和matplotlib 1.2.0]

答案 3 :(得分:18)

另一种易于使用的解决方案是使用suptitle调用中的y参数调整图中suptitle文本的坐标(参见docs):

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

答案 4 :(得分:4)

我一直在努力使用matplotlib修剪方法,所以我现在只是通过bash调用ImageMagick的{​​{3}}来实现这一功能,效果很好,从图的边缘获得所有额外的空白区域。这要求您使用UNIX / Linux,使用bash shell,并安装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)

答案 5 :(得分:2)

This website通过适用于我的示例对此提供了一个简单的解决方案。实际保留标题空间的代码行如下:

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

以下是证明它对我有用的证据: Image Link

答案 6 :(得分:1)

截至 v3.3 tight_layout 现在支持 suptitle

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 3)
for i, ax in enumerate(axs):
    ax.plot([1, 2, 3])
    ax.set_title(f'Axes {i}')

fig.suptitle('suptitle')
fig.tight_layout()

enter image description here

答案 7 :(得分:0)

我遇到类似的问题,当使用tight_layout处理非常大的网格图(超过200个子图)并在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)

对于可变大小的子图,您仍然可以使用此方法获得最顶层子图的顶部,然后手动定义要添加到字幕中的其他量。

答案 8 :(得分:0)

正如其他人所提到的,默认情况下,紧密布局不考虑字幕。但是,我发现可以使用bbox_extra_artists参数来传递字幕作为应该考虑的边界框:

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

这会强制进行紧凑的布局计算,以考虑suptitle,并且看起来像您期望的那样。

答案 9 :(得分:0)

紧密布局不适用于字幕,但是constrained_layout可以。看到这个问题Improve subplot size/spacing with many subplots in matplotlib

我发现立即添加子图看起来更好,即

fig, axs = plt.subplots(rows, cols, contrained_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

答案 10 :(得分:0)

对我唯一有用的是修改对字幕的调用:

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