强基初中数学&学Python——第266课 数字和数学第三方模块Matplotlib之七:封装端-紧凑布局指南

  如何使用紧凑布局清晰地绘制符合要求的图表。

  紧凑布局自动调整subplot参数,使subplot(s)适合图表区域。这是一个试验特性,在某些情况下可能不起作用。它只检查刻度标签、轴标签和标题的范围。

  紧凑布局的一种替代是约束布局(constrained_layout)。

简单示例

  matplotlib中,图表域(axes,包括子图表subplots)的位置在标准化图表(figure)坐标中指定。轴标签或标题(有时甚至是刻度标签)可能会超出图形区域,从而被剪裁。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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)    fig, ax = plt.subplots()example_plot(ax, fontsize=24)

 

  为了防止这种情况,需要调整图表域(axes)的位置。对于子图表(subplots),可以使用Figure.subplots_adjust手动调整它参数。Figure.tight_layout自动执行此操作。

· 

· 

· 

fig, ax = plt.subplots()example_plot(ax, fontsize=24)plt.tight_layout()

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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)    fig, ax = plt.subplots()example_plot(ax, fontsize=24)plt.tight_layout()

 

  请注意,matplotlib.pyplot.tight_layout()仅在调用时调整subplot参数。为了在每次重新绘制图表时执行此调整,可以调用fig.set_tight_layout(True),或者等效地将rcParams["figure.autolayout"] (默认值:False)设置为True。

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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)   fig, ax = plt.subplots()fig.set_tight_layout(True)example_plot(ax, fontsize=24)

 

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as pltplt.rcParams["figure.autolayout"] = True
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)   fig, ax = plt.subplots()example_plot(ax, fontsize=24)

 

  当绘制多个子图表时,通常会看到不同图表域(axes)的标签彼此重叠。

· 

· 

· 

· 

· 

· 

· 

plt.close('all')
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)example_plot(ax1)example_plot(ax2)example_plot(ax3)example_plot(ax4)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)example_plot(ax1)example_plot(ax2)example_plot(ax3)example_plot(ax4)

 

  tight_layout() 还可以调整子图表之间的间距,以使重叠最少。

· 

· 

· 

· 

· 

· 

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)example_plot(ax1)example_plot(ax2)example_plot(ax3)example_plot(ax4)plt.tight_layout()

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)example_plot(ax1)example_plot(ax2)example_plot(ax3)example_plot(ax4)plt.tight_layout()

 

tight_layout() 可以接受 pad、w_pad和h_pad的关键字参数。这些控件控制图表边界周围和子图表(subplots)之间的额外填充。填充以字体大小的分数指定。

· 

· 

· 

· 

· 

· 

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)example_plot(ax1)example_plot(ax2)example_plot(ax3)example_plot(ax4)plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)example_plot(ax1)example_plot(ax2)example_plot(ax3)example_plot(ax4)plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)

 

  即使子图表的大小不同,只要其网格规范兼容,tight_layout()也可以工作。在下面的示例中,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()

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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 = 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()创建的子图表(subplots)。通常,从gridspec(在图表中排列多个图表域,Arranging multiple Axes in a Figure)创建的子图表将起作用。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

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

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython
%matplotlib tkimport matplotlib.pyplot as plt
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 = 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()

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport numpy as np
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)会考虑图表域(axes)上的所有艺术家(artist)。要从布局计算中忽略某个艺术家,可以调用Artist.set_in_layout(True/False)(本文后面有例子)。

  ● 紧凑布局假设艺术家所需的额外空间独立于图表域(axes)的原始位置。这通常是正确的,但有极少情况不是。

  ● pad=0可以将一些文本裁剪几个像素。这可能是当前算法的错误或限制,目前尚不清楚为什么会发生这种情况。同时,建议使用大于0.3的pad。

 

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)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.gridspec as gridspec
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 = 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(矩形)参数,该参数指定子图表(subplot)将放在其中的边界框。坐标必须为标准化图表(figure)坐标,默认值为(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])

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.gridspec as gridspec
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 = 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。对于这些用例,应该使用嵌套的Gridpec(Nested Gridspecs)或图表的子图表(Figure subfigures)。

图例和注释

  Matplotlib 2.2之前,图例和注释被排除在决定布局的边界框计算之外。之后的版本,这些艺术家被添加到计算中,但有时不希望包括他们。例如,在这种情况下,最好将图表域(axes)缩小一点,以便为图例腾出空间:

· 

· 

· 

· 

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

  完整代码:

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltfig, 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()

 

  然而,有时这是不需要这样的(在使用fig.savefig('outname.png',bbox_inches='ight')时经常如此)。为了从边界框计算中忽略图例,只需设置其边界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()

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltfig, 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()

 

 

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

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltfrom mpl_toolkits.axes_grid1 import Grid
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 = 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创建了一个色条,则只要父图表域(axes)也是子图表(Subplot),所创建的色条也在子图表中绘制,因此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()

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport numpy as np
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工具包为色条显式创建图表域(Axes)。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

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

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport numpy as npfrom 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()

 

 

constrained_layout:

https://matplotlib.org/stable/tutorials/intermediate/constrainedlayout_guide.html

Figure.subplots_adjust:

https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.subplots_adjust

Figure.tight_layout:

https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.tight_layout

matplotlib.pyplot.tight_layout():

https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout

rcParams["figure.autolayout"]:

https://matplotlib.org/stable/tutorials/introductory/customizing.html?highlight=figure.autolayout#matplotlibrc-sample

tight_layout():

https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout

subplot2grid():

https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplot2grid.html#matplotlib.pyplot.subplot2grid

Arranging multiple Axes in a Figure:

https://matplotlib.org/stable/tutorials/intermediate/arranging_axes.html

Artist.set_in_layout:

https://matplotlib.org/stable/api/_as_gen/matplotlib.artist.Artist.set_in_layout.html#matplotlib.artist.Artist.set_in_layout

GridSpec.tight_layout:

https://matplotlib.org/stable/api/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec.tight_layout

pyplot.tight_layout:

https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.tight_layout.html#matplotlib.pyplot.tight_layout

mpl_toolkits.axes_grid1:

https://matplotlib.org/stable/api/toolkits/axes_grid1.html#module-mpl_toolkits.axes_grid1

Figure.colorbar:

https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.colorbar