注意
转到页面底部下载完整示例代码。
紧凑布局指南#
如何使用紧凑布局(tight-layout)将绘图整齐地适配到画布中。
提示
tight_layout 是 Matplotlib 中最早的布局引擎。通常应优先使用更现代、功能更强的 约束布局 (Constrained Layout)。
tight_layout 会自动调整子图参数,使子图能够适配画布区域。这是一个实验性功能,在某些情况下可能无法正常工作。它仅检查刻度标签、轴标签和标题的范围。
简单示例#
使用默认的轴(Axes)定位时,轴标题、轴标签或刻度标签有时会超出画布区域,从而被裁剪掉。
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"
def example_plot(ax, fontsize=12):
ax.plot([1, 2])
ax.locator_params(nbins=3)
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
plt.close('all')
fig, ax = plt.subplots()
example_plot(ax, fontsize=24)

为防止这种情况,需要调整轴的位置。对于子图,可以通过使用 Figure.subplots_adjust 手动调整子图参数来实现。而 Figure.tight_layout 可以自动完成此操作。
fig, ax = plt.subplots()
example_plot(ax, fontsize=24)
plt.tight_layout()

注意,matplotlib.pyplot.tight_layout() 仅在调用时才会调整子图参数。为了在每次重绘图形时都执行此调整,可以调用 fig.set_tight_layout(True),或者将 rcParams["figure.autolayout"](默认值:False)设置为 True。
当有多个子图时,经常会看到不同轴的标签相互重叠。

tight_layout() 还会调整子图之间的间距,以最大限度地减少重叠。

tight_layout() 可以接收 pad、w_pad 和 h_pad 关键字参数。它们用于控制画布边框周围以及子图之间的额外填充。这些填充值以字体大小的倍数指定。

tight_layout() 即使在子图大小不同的情况下也能工作,只要它们的网格规格(grid specification)兼容即可。在下面的示例中,ax1 和 ax2 是 2x2 网格中的子图,而 ax3 是 1x2 网格中的子图。
plt.close('all')
fig = plt.figure()
ax1 = plt.subplot(221)
ax2 = plt.subplot(223)
ax3 = plt.subplot(122)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
plt.tight_layout()

它适用于使用 subplot2grid() 创建的子图。通常,由 gridspec 创建的子图(在画布中排列多个轴)都可以正常工作。
plt.close('all')
fig = plt.figure()
ax1 = plt.subplot2grid((3, 3), (0, 0))
ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
example_plot(ax4)
plt.tight_layout()

虽然未经全面测试,但它似乎也适用于 aspect != "auto" 的子图(例如包含图像的轴)。
arr = np.arange(100).reshape((10, 10))
plt.close('all')
fig = plt.figure(figsize=(5, 4))
ax = plt.subplot()
im = ax.imshow(arr, interpolation="none")
plt.tight_layout()

注意事项#
默认情况下,
tight_layout会考虑轴上的所有艺术家(artist)对象。要从布局计算中移除某个对象,可以调用Artist.set_in_layout。tight_layout假设艺术家所需的额外空间与其轴的原始位置无关。这通常是正确的,但在极少数情况下可能并非如此。pad=0可能会导致某些文本被裁剪掉几个像素。这可能是算法的一个缺陷或局限性,目前尚不清楚原因。建议设置大于 0.3 的填充值。tight_layout的算法并不一定会收敛,这意味着多次调用tight_layout可能会导致每次调用之间的布局存在细微差异。
与 GridSpec 一起使用#
GridSpec 有其自己的 GridSpec.tight_layout 方法(pyplot API 中的 pyplot.tight_layout 也同样有效)。
import matplotlib.gridspec as gridspec
plt.close('all')
fig = plt.figure()
gs1 = gridspec.GridSpec(2, 1)
ax1 = fig.add_subplot(gs1[0])
ax2 = fig.add_subplot(gs1[1])
example_plot(ax1)
example_plot(ax2)
gs1.tight_layout(fig)

您可以提供一个可选的 rect 参数,用于指定子图应适配的边界框。坐标为归一化画布坐标,默认为 (0, 0, 1, 1)(整个画布)。
fig = plt.figure()
gs1 = gridspec.GridSpec(2, 1)
ax1 = fig.add_subplot(gs1[0])
ax2 = fig.add_subplot(gs1[1])
example_plot(ax1)
example_plot(ax2)
gs1.tight_layout(fig, rect=[0, 0, 0.5, 1.0])

然而,我们不建议将其用于手动构建复杂的布局,例如在画布左侧放置一个 GridSpec,右侧放置另一个。对于这些用例,应该利用 嵌套 Gridspec 或 画布子图 (Figure subfigures)。
图例与标注#
在 Matplotlib 2.2 之前,图例和标注不参与决定布局的边界框计算。后来,这些艺术家对象被加入计算中,但有时包含它们并不理想。例如,在这种情况下,为了给图例留出空间而缩小轴可能会更好。
fig, ax = plt.subplots(figsize=(4, 3))
lines = ax.plot(range(10), label='A simple plot')
ax.legend(bbox_to_anchor=(0.7, 0.5), loc='center left',)
fig.tight_layout()
plt.show()

然而,有时我们并不希望这样做(通常在使用 fig.savefig('outname.png', bbox_inches='tight') 时)。为了将图例从边界框计算中移除,我们只需设置 leg.set_in_layout(False),这样图例就会被忽略。
fig, ax = plt.subplots(figsize=(4, 3))
lines = ax.plot(range(10), label='B simple plot')
leg = ax.legend(bbox_to_anchor=(0.7, 0.5), loc='center left',)
leg.set_in_layout(False)
fig.tight_layout()
plt.show()

与 AxesGrid1 一起使用#
对 mpl_toolkits.axes_grid1 提供有限的支持。
from mpl_toolkits.axes_grid1 import Grid
plt.close('all')
fig = plt.figure()
grid = Grid(fig, rect=111, nrows_ncols=(2, 2),
axes_pad=0.25, label_mode='L',
)
for ax in grid:
example_plot(ax)
ax.title.set_visible(False)
plt.tight_layout()

颜色条(Colorbar)#
如果您使用 Figure.colorbar 创建颜色条,只要父轴也是子图,创建的颜色条就会绘制在子图中,因此 Figure.tight_layout 将正常工作。
plt.close('all')
arr = np.arange(100).reshape((10, 10))
fig = plt.figure(figsize=(4, 4))
im = plt.imshow(arr, interpolation="none")
plt.colorbar(im)
plt.tight_layout()

另一种选择是使用 AxesGrid1 工具包显式地为颜色条创建轴。
from mpl_toolkits.axes_grid1 import make_axes_locatable
plt.close('all')
arr = np.arange(100).reshape((10, 10))
fig = plt.figure(figsize=(4, 4))
im = plt.imshow(arr, interpolation="none")
divider = make_axes_locatable(plt.gca())
cax = divider.append_axes("right", "5%", pad="3%")
plt.colorbar(im, cax=cax)
plt.tight_layout()

脚本总运行时间:(0 分 5.429 秒)