强基初中数学&学Python——第263课 数字和数学第三方模块Matplotlib之七:封装端-约束布局指南(2)

填充(Padding)和间距(Spacing

  图表域之间的填充空间在水平方向上由w_padwspace控制,在垂直方向上由h_padhspace控制。这些可以通过set函数进行编辑。w/h_pad是图表域(axes)周围的最小间距,单位为英寸:

· 

· 

· 

· 

· 

fig, axs = plt.subplots(2, 2, layout="constrained")for ax in axs.flat:    example_plot(ax, hide_labels=True)fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0,                            wspace=0)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50

def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([1, 2])
    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)
fig, axs = plt.subplots(2, 2, layout="constrained")for ax in axs.flat:    example_plot(ax, hide_labels=True)fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0,                            wspace=0)

 

  子图表之间的间距由wspacehspace进一步设置。这些被指定为整个子图表组(一行或一列子图表)大小的一部分。如果这些值小于w_padh_pad,则使用固定填充空间。请注意,通过观察下面的图表,会发现边缘处的空间与上面的图表没有变化,但子图表之间的空间发生了变化。这就是w/hspacew/h_pad的不同。

· 

· 

· 

· 

· 

fig, axs = plt.subplots(2, 2, layout="constrained")for ax in axs.flat:    example_plot(ax, hide_labels=True)fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2,                            wspace=0.2)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50

def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([1, 2])
    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)
fig, axs = plt.subplots(22, layout="constrained")fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2,                            wspace=0.2)for ax in axs.flat:    example_plot(ax, hide_labels=True)

 

  如果超过两列,则wspace在列间隔之间共享,因此下面的例子,wspace被分成两份,每份的wspace0.1

· 

· 

· 

· 

· 

fig, axs = plt.subplots(2, 3, layout="constrained")for ax in axs.flat:    example_plot(ax, hide_labels=True)fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2,                            wspace=0.2)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50

def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([1, 2])
    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)fig, axs = plt.subplots(23, layout="constrained")fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2,                            wspace=0.2)for ax in axs.flat:    example_plot(ax, hide_labels=True)

 

  GridSpec还具有可选的hspacewspace关键字参数,这些参数将用于代替constrained_layout设置的pad

· 

· 

· 

· 

· 

· 

· 

· 

fig, axs = plt.subplots(2, 2, layout="constrained",                        gridspec_kw={'wspace': 0.3, 'hspace': 0.2})# this has no effect because the space set in the gridspec trumps the# space set in constrained_layout.fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.0,                            wspace=0.0)for ax in axs.flat:    example_plot(ax, hide_labels=True)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50

def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([1, 2])
    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)fig, axs = plt.subplots(2, 2, layout="constrained",                        gridspec_kw={'wspace': 0.3, 'hspace': 0.2})# this has no effect because the space set in the gridspec trumps the# space set in constrained_layout.fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.0,                            wspace=0.0)for ax in axs.flat:    example_plot(ax, hide_labels=True)

 

 

与色条的间距

  色条与父对象之间有一定距离的填充空间(pad),该填充空间是父对象宽度的一部分。然后通过w/hspace给出到下一个子图表的间距。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

fig, axs = plt.subplots(2, 2, layout="constrained")pads = [0, 0.05, 0.1, 0.2]for pad, ax in zip(pads, axs.flat):    pc = ax.pcolormesh(arr, **pc_kwargs)    fig.colorbar(pc, ax=ax, shrink=0.6, pad=pad)    ax.set_xticklabels([])    ax.set_yticklabels([])    ax.set_title(f'pad: {pad}')fig.get_layout_engine().set(w_pad=2 / 72, h_pad=2 / 72, hspace=0.2,                            wspace=0.2)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport numpy as nparr = np.arange(100).reshape((10, 10))norm = mcolors.Normalize(vmin=0., vmax=100.)# see note above: this makes all pcolormesh calls consistent:pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}fig, axs = plt.subplots(2, 2, layout="constrained")pads = [0, 0.05, 0.1, 0.2]for pad, ax in zip(pads, axs.flat):    pc = ax.pcolormesh(arr, **pc_kwargs)    fig.colorbar(pc, ax=ax, shrink=0.6, pad=pad)    ax.set_xticklabels([])    ax.set_yticklabels([])    ax.set_title(f'pad: {pad}')fig.get_layout_engine().set(w_pad=2 / 72, h_pad=2 / 72, hspace=0.2,                            wspace=0.2)

 

 

rcParamsrc参数)

  可以在脚本或matplotlibrc文件中设置五个rcParams。它们都有前缀figure.constrained_layout

  ● use:是否使用constrained_layout。默认值为False

  ● w_padh_pad:围绕图表域(axes)对象填充空白。表示英寸的浮点数。默认值为3./72。英寸(3分)

  ● wspacehspace:子图表组之间的空隔。浮点数表示要分隔的子图表宽度的一部分。默认值为0.02

· 

· 

· 

· 

plt.rcParams['figure.constrained_layout.use'] = Truefig, axs = plt.subplots(2, 2, figsize=(3, 3))for ax in axs.flat:    example_plot(ax)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50
def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([12])    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)plt.rcParams['figure.constrained_layout.use'] = Truefig, axs = plt.subplots(2, 2, figsize=(3, 3))for ax in axs.flat:    example_plot(ax)

 

 

与网格排版(GridSpec)一起使用

  约束布局用于subplots()subplot_mosaic()或用于带GridSpec()add_subplot()

  注意,在以下布局中,layout="constrained"

· 

· 

· 

· 

· 

· 

· 

· 

· 

plt.rcParams['figure.constrained_layout.use'] = Falsefig = plt.figure(layout="constrained")
gs1 = gridspec.GridSpec(2, 1, figure=fig)ax1 = fig.add_subplot(gs1[0])ax2 = fig.add_subplot(gs1[1])
example_plot(ax1)example_plot(ax2)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50
def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([12])    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)plt.rcParams['figure.constrained_layout.use'] = Falsefig = plt.figure(layout="constrained")
gs1 = gridspec.GridSpec(2, 1, figure=fig)ax1 = fig.add_subplot(gs1[0])ax2 = fig.add_subplot(gs1[1])
example_plot(ax1)example_plot(ax2)

 

  更复杂的网格布局是可能的。注意这里我们使用了便利函数add_gridspecsubgridspec

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

fig = plt.figure(layout="constrained")
gs0 = fig.add_gridspec(1, 2)
gs1 = gs0[0].subgridspec(2, 1)ax1 = fig.add_subplot(gs1[0])ax2 = fig.add_subplot(gs1[1])
example_plot(ax1)example_plot(ax2)
gs2 = gs0[1].subgridspec(3, 1)
for ss in gs2:    ax = fig.add_subplot(ss)    example_plot(ax)    ax.set_title("")    ax.set_xlabel("")
ax.set_xlabel("x-label", fontsize=12)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50
def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([12])    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)fig = plt.figure(layout="constrained")
gs0 = fig.add_gridspec(1, 2)
gs1 = gs0[0].subgridspec(2, 1)ax1 = fig.add_subplot(gs1[0])ax2 = fig.add_subplot(gs1[1])
example_plot(ax1)example_plot(ax2)
gs2 = gs0[1].subgridspec(3, 1)
for ss in gs2:    ax = fig.add_subplot(ss)    example_plot(ax)    ax.set_title("")    ax.set_xlabel("")
ax.set_xlabel("x-label", fontsize=12)

 

  请注意,在上面的图表中,左列和右列的垂直范围不相同。如果我们希望两个网格的顶部和底部对齐,那么它们需要在同一个网格布局中。我们还需要将此图表放大,以使图表域(axes)不会塌陷到零高度:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

fig = plt.figure(figsize=(4, 6), layout="constrained")
gs0 = fig.add_gridspec(6, 2) #6行2列
ax1 = fig.add_subplot(gs0[:3, 0]) #合并第1列的第1行到第3行ax2 = fig.add_subplot(gs0[3:, 0]) #合并第1列的第4行到第6行
example_plot(ax1)example_plot(ax2)
ax = fig.add_subplot(gs0[0:2, 1]) #合并第2列的第1行到第2行example_plot(ax, hide_labels=True)ax = fig.add_subplot(gs0[2:4, 1]) #合并第2列的第3行到第4行example_plot(ax, hide_labels=True)ax = fig.add_subplot(gs0[4:, 1]) #合并第2列的第5行到第6行example_plot(ax, hide_labels=True)fig.suptitle('Overlapping Gridspecs')

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50
def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([12])    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)
fig = plt.figure(figsize=(4, 6), layout="constrained")
gs0 = fig.add_gridspec(6, 2)
ax1 = fig.add_subplot(gs0[:3, 0])ax2 = fig.add_subplot(gs0[3:, 0])
example_plot(ax1)example_plot(ax2)
ax = fig.add_subplot(gs0[0:2, 1])example_plot(ax, hide_labels=True)ax = fig.add_subplot(gs0[2:4, 1])example_plot(ax, hide_labels=True)ax = fig.add_subplot(gs0[4:, 1])example_plot(ax, hide_labels=True)fig.suptitle('Overlapping Gridspecs')

 

  下面的示例使用两个网格,以使色条仅适用于一组颜色。请注意,由于这一点,左侧的列比右侧的两列宽。当然,如果你想让子图表大小相同,你只需要一个网格布局。请注意,使用subfigures(子图表)可以实现相同的效果。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

fig = plt.figure(layout="constrained")gs0 = fig.add_gridspec(1, 2, figure=fig, width_ratios=[1, 2])gs_left = gs0[0].subgridspec(2, 1)gs_right = gs0[1].subgridspec(2, 2)
for gs in gs_left:    ax = fig.add_subplot(gs)    example_plot(ax)axs = []for gs in gs_right:    ax = fig.add_subplot(gs)    pcm = ax.pcolormesh(arr, **pc_kwargs)    ax.set_xlabel('x-label')    ax.set_ylabel('y-label')    ax.set_title('title')    axs += [ax]fig.suptitle('Nested plots using subgridspec')fig.colorbar(pcm, ax=axs)

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as nparr = np.arange(100).reshape((10, 10))norm = mcolors.Normalize(vmin=0., vmax=100.)# see note above: this makes all pcolormesh calls consistent:pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50
def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([12])    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)
fig = plt.figure(layout="constrained")gs0 = fig.add_gridspec(1, 2, figure=fig, width_ratios=[1, 2])gs_left = gs0[0].subgridspec(2, 1)gs_right = gs0[1].subgridspec(2, 2)
for gs in gs_left:    ax = fig.add_subplot(gs)    example_plot(ax)axs = []for gs in gs_right:    ax = fig.add_subplot(gs)    pcm = ax.pcolormesh(arr, **pc_kwargs)    ax.set_xlabel('x-label')    ax.set_ylabel('y-label')    ax.set_title('title')    axs += [ax]fig.suptitle('Nested plots using subgridspec')fig.colorbar(pcm, ax=axs)

 

  除了subgridspecMatplotlib现在也提供了subfigures(子图表)功能,同样使用父图表的布局(约束布局):

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

fig = plt.figure(layout="constrained")sfigs = fig.subfigures(1, 2, width_ratios=[1, 2])
axs_left = sfigs[0].subplots(2, 1)for ax in axs_left.flat:    example_plot(ax)
axs_right = sfigs[1].subplots(2, 2)for ax in axs_right.flat:    pcm = ax.pcolormesh(arr, **pc_kwargs)    ax.set_xlabel('x-label')    ax.set_ylabel('y-label')    ax.set_title('title')fig.colorbar(pcm, ax=axs_right)fig.suptitle('Nested plots using subfigures')

  完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

ipython%matplotlib tkimport matplotlib.pyplot as pltimport matplotlib.colors as mcolorsimport matplotlib.gridspec as gridspecimport numpy as nparr = np.arange(100).reshape((10, 10))norm = mcolors.Normalize(vmin=0., vmax=100.)# see note above: this makes all pcolormesh calls consistent:pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
plt.rcParams['savefig.facecolor'] = "0.8"plt.rcParams['figure.figsize'] = 4.5, 4.plt.rcParams['figure.max_open_warning'] = 50
def example_plot(ax, fontsize=12, hide_labels=False):    ax.plot([12])    ax.locator_params(nbins=3)    if hide_labels:        ax.set_xticklabels([])        ax.set_yticklabels([])    else:        ax.set_xlabel('x-label', fontsize=fontsize)        ax.set_ylabel('y-label', fontsize=fontsize)        ax.set_title('Title', fontsize=fontsize)
fig = plt.figure(layout="constrained")sfigs = fig.subfigures(1, 2, width_ratios=[1, 2])
axs_left = sfigs[0].subplots(2, 1)for ax in axs_left.flat:    example_plot(ax)
axs_right = sfigs[1].subplots(2, 2)for ax in axs_right.flat:    pcm = ax.pcolormesh(arr, **pc_kwargs)    ax.set_xlabel('x-label')    ax.set_ylabel('y-label')    ax.set_title('title')fig.colorbar(pcm, ax=axs_right)fig.suptitle('Nested plots using subfigures')

 

 

set:

https://matplotlib.org/stable/api/layout_engine_api.html#matplotlib.layout_engine.ConstrainedLayoutEngine.set

rcParams:

https://matplotlib.org/stable/tutorials/introductory/customizing.html#customizing-with-dynamic-rc-settings

subplots():

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

subplot_mosaic():

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

GridSpec():

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

add_subplot():

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

add_gridspec:

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

subgridspec:

https://matplotlib.org/stable/api/_as_gen/matplotlib.gridspec.SubplotSpec.html#matplotlib.gridspec.SubplotSpec.subgridspec

subfigures:

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