简体   繁体   English

Matplotlib tiny_layout() 不考虑图形 suptitle

[英]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 修剪方法,所以我现在只是通过对ImageMagickmogrify 命令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)

As of v3.3 tight_layout now supports suptitle :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()

在此处输入图片说明

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM