[英]Matplotlib tight_layout() doesn't take into account figure suptitle
If I add a subtitle to my matplotlib figure it gets overlaid by the subplot's titles.如果我为 matplotlib 图形添加副标题,它会被子图的标题覆盖。 Does anybody know how to easily take care of that?
有谁知道如何轻松解决这个问题? I tried the
tight_layout()
function, but it only makes things worse.我尝试了
tight_layout()
函数,但它只会让事情变得更糟。
Example:例子:
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()
You can adjust the subplot geometry in the very tight_layout
call as follows:您可以在非常
tight_layout
调用中调整子图几何,如下所示:
fig.tight_layout(rect=[0, 0.03, 1, 0.95])
As it's stated in the documentation ( https://matplotlib.org/users/tight_layout_guide.html ):正如文档中所述( https://matplotlib.org/users/tight_layout_guide.html ):
tight_layout()
only considers ticklabels, axis labels, and titles.tight_layout()
只考虑刻度标签、轴标签和标题。 Thus, other artists may be clipped and also may overlap.因此,其他艺术家可能会被剪辑,也可能会重叠。
You could manually adjust the spacing using plt.subplots_adjust(top=0.85)
:您可以使用
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()
One thing you could change in your code very easily is the fontsize
you are using for the titles.您可以很容易地在代码中更改的一件事是您用于标题的
fontsize
大小。 However, I am going to assume that you don't just want to do that!但是,我将假设您不只是想这样做!
Some alternatives to using fig.subplots_adjust(top=0.85)
:使用
fig.subplots_adjust(top=0.85)
一些替代方法:
Usually tight_layout()
does a pretty good job at positioning everything in good locations so that they don't overlap.通常,
tight_layout()
在将所有内容定位在tight_layout()
位置方面做得非常好,这样它们就不会重叠。 The reason tight_layout()
doesn't help in this case is because tight_layout()
does not take fig.suptitle() into account.在这种情况下
tight_layout()
没有帮助的原因是tight_layout()
没有考虑 fig.suptitle() 。 There is an open issue about this on GitHub: https://github.com/matplotlib/matplotlib/issues/829 [closed in 2014 due to requiring a full geometry manager - shifted to https://github.com/matplotlib/matplotlib/issues/1109 ].在 GitHub 上有一个关于此的未决问题: https : //github.com/matplotlib/matplotlib/issues/829 [由于需要完整的几何管理器而于 2014 年关闭 - 转移到https://github.com/matplotlib/matplotlib /issues/1109 ]。
If you read the thread, there is a solution to your problem involving GridSpec
.如果您阅读了该线程,则可以解决涉及
GridSpec
的问题。 The key is to leave some space at the top of the figure when calling tight_layout
, using the rect
kwarg.关键是在使用
rect
kwarg 调用tight_layout
时在图的顶部留出一些空间。 For your problem, the code becomes:对于您的问题,代码变为:
Using GridSpec使用 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()
The result:结果:
Maybe GridSpec
is a bit overkill for you, or your real problem will involve many more subplots on a much larger canvas, or other complications.也许
GridSpec
对您来说有点矫枉过正,或者您的真正问题将涉及更大画布上的更多子图,或其他复杂情况。 A simple hack is to just use annotate()
and lock the coordinates to the 'figure fraction'
to imitate a suptitle
.一个简单的技巧是只使用
annotate()
并将坐标锁定到'figure fraction'
以模仿suptitle
。 You may need to make some finer adjustments once you take a look at the output, though.但是,一旦查看输出,您可能需要进行一些更精细的调整。 Note that this second solution does not use
tight_layout()
.请注意,这第二个解决方案不使用
tight_layout()
Simpler solution (though may need to be fine-tuned)更简单的解决方案(虽然可能需要微调)
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()
The result:结果:
[Using Python
2.7.3 (64-bit) and matplotlib
1.2.0] [使用
Python
2.7.3(64 位)和matplotlib
1.2.0]
An alternative and simple to use solution is to adjust the coordinates of the suptitle text in the figure using the y argument in the call of suptitle (see the docs ):另一种简单易用的解决方案是使用 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()
Tight layout doesn't work with suptitle, but constrained_layout
does.紧密布局不适用于 suptitle,但
constrained_layout
可以。 See this question Improve subplot size/spacing with many subplots in matplotlib看到这个问题用 matplotlib 中的许多子图改进子图大小/间距
I found adding the subplots at once looked better, ie我发现立即添加子图看起来更好,即
fig, axs = plt.subplots(rows, cols, constrained_layout=True)
# then iterating over the axes to fill in the plots
But it can also be added at the point the figure is created:但也可以在创建图形时添加:
fig = plt.figure(constrained_layout=True)
ax1 = fig.add_subplot(cols, rows, 1)
# etc
Note: To make my subplots closer together, I was also using注意:为了使我的子图更接近,我还使用了
fig.subplots_adjust(wspace=0.05)
and constrained_layout doesn't work with this :(并且受约束的布局不适用于此:(
As mentioned by others, by default the tight layout does not take suptitle into account.正如其他人所提到的,默认情况下,紧密布局不考虑 suptitle。 However, I have found it is possible to use the
bbox_extra_artists
argument to pass in the suptitle as a bounding box that should be taken into account:但是,我发现可以使用
bbox_extra_artists
参数将 suptitle 作为应考虑的边界框传递:
st = fig.suptitle("My Super Title")
plt.savefig("figure.png", bbox_extra_artists=[st], bbox_inches='tight')
This forces the tight layout calculation to take the suptitle
into account, and it looks as you would expect.这会强制紧凑的布局计算将
suptitle
考虑在内,并且它看起来如您suptitle
。
I have struggled with the matplotlib trimming methods, so I've now just made a function to do this via a bash
call to ImageMagick
's mogrify command , which works well and gets all extra white space off the figure's edge.我一直在努力使用 matplotlib 修剪方法,所以我现在只是通过对
ImageMagick
的mogrify 命令的bash
调用创建了一个函数来执行此操作,该命令运行良好并从图形边缘获取所有额外的空白。 This requires that you are using UNIX/Linux, are using the bash
shell, and have ImageMagick
installed.这要求您使用 UNIX/Linux,使用
bash
shell 并安装ImageMagick
。
Just throw a call to this after your savefig()
call.只需在
savefig()
调用后调用此方法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)
This website has a simple solution to this with an example that worked for me. 这个网站有一个简单的解决方案,有一个对我有用的例子。 The line of code that does the actual leaving of space for the title is the following:
为标题实际留出空间的代码行如下:
plt.tight_layout(rect=[0, 0, 1, 0.95])
Here is an image of proof that it worked for me:这是一张证明它对我有用的图片:
唯一对我有用的是修改对 suptitle 的调用:
fig.suptitle("title", y=.995)
I had a similar issue that cropped up when using tight_layout
for a very large grid of plots (more than 200 subplots) and rendering in a jupyter notebook.我有一个类似的问题,当使用
tight_layout
处理一个非常大的图网格(超过 200 个子图)并在 jupyter notebook 中渲染时出现了类似的问题。 I made a quick solution that always places your suptitle
at a certain distance above your top subplot:我做了一个快速的解决方案,总是将您的
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)
For variably-sized subplots, you can still use this method to get the top of the topmost subplot, then manually define an additional amount to add to the suptitle.对于可变大小的子图,您仍然可以使用此方法获取最顶层子图的顶部,然后手动定义一个额外的数量以添加到 suptitle。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.