自行设置图表域(axes)位置
有充分的理由自行设置图表域(axes)位置。自行调用set_position将设置图表域(axes),因此约束布局不再对该图表域产生影响。(请注意,约束布局仍为移动的图表域保留间距(space))。
·
·
·
fig, axs = plt.subplots(1, 2, layout="constrained")example_plot(axs[0], fontsize=12)axs[1].set_position([0.2, 0.2, 0.4, 0.4])
完整代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
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(1, 2, layout="constrained")example_plot(axs[0], fontsize=12)axs[1].set_position([0.2, 0.2, 0.4, 0.4])
固定纵横比图表域(Axes)的网格:“压缩”布局
约束布局在图表域(axes)的“原始”位置网格上运行。然而,当图表域(Axes)具有固定的纵横比时,一侧通常会变短,并在缩短的方向上留下较大的间隙。在下文中,图表域(Axes)是方形的,但图表(figure)相当宽,因此存在水平间隙:
·
·
·
·
·
fig, axs = plt.subplots(2, 2, figsize=(5, 3), sharex=True, sharey=True, layout="constrained")for ax in axs.flat: ax.imshow(arr)fig.suptitle("fixed-aspect plots, layout='constrained'")
完整代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
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, figsize=(5, 3), sharex=True, sharey=True, layout="constrained")for ax in axs.flat: ax.imshow(arr)fig.suptitle("fixed-aspect plots, layout='constrained'")
解决这一问题的一个明显方法是使图表大小更为方正,然而,缩小差距恰恰需要反复尝试。对于简单的图表域(Axes)网格,我们可以使用layout="compressed"来完成这项工作:
·
·
·
·
·
fig, axs = plt.subplots(2, 2, figsize=(5, 3), sharex=True, sharey=True, layout='compressed')for ax in axs.flat: ax.imshow(arr)fig.suptitle("fixed-aspect plots, layout='compressed'")
完整代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
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, figsize=(5, 3), sharex=True, sharey=True, layout='compressed')for ax in axs.flat: ax.imshow(arr)fig.suptitle("fixed-aspect plots, layout='compressed'")
手动关闭约束布局
在每次绘制图表(figure)时,约束布局通常会调整每个图表域(axes)位置。如果想获得约束布局提供的间距,但不更新它,那么进行初始绘制后调用fig.set_layout_engine(None)关闭布局。这对于刻度标签可能会改变长度的动画可能很有用。
另外,对于后端工具栏的缩放(ZOOM)和平移(PAN)的图形界面(GUI)事件,约束布局也已关闭,这样可防止图表域在缩放和平移过程中改变位置。
局限性
不兼容的功能
约束布局将使用pyplot.subplot,但前提是每次调用的行数和列数相同。原因是,如果几何图形不相同,每次调用pyplot.subplot都会创建一个新的GridSpec实例,并且再进行约束布局。因此,以下操作正常:
·
·
·
·
·
·
·
·
·
·
·
fig = plt.figure(layout="constrained")
ax1 = plt.subplot(2, 2, 1)ax2 = plt.subplot(2, 2, 3)# third axes that spans both rows in second column:ax3 = plt.subplot(2, 2, (2, 4))
example_plot(ax1)example_plot(ax2)example_plot(ax3)plt.suptitle('Homogenous nrows, ncols')
完整代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
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 = plt.figure(layout="constrained")
ax1 = plt.subplot(2, 2, 1)ax2 = plt.subplot(2, 2, 3)# third axes that spans both rows in second column:ax3 = plt.subplot(2, 2, (2, 4))
example_plot(ax1)example_plot(ax2)example_plot(ax3)plt.suptitle('Homogenous nrows, ncols')
但下面的代码就会导致差劲的布局。
·
·
·
·
·
·
·
·
·
·
fig = plt.figure(layout="constrained")
ax1 = plt.subplot(2, 2, 1)ax2 = plt.subplot(2, 2, 3)ax3 = plt.subplot(1, 2, 2)
example_plot(ax1)example_plot(ax2)example_plot(ax3)plt.suptitle('Mixed nrows, ncols')
完整代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
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 = plt.figure(layout="constrained")
ax1 = plt.subplot(2, 2, 1)ax2 = plt.subplot(2, 2, 3)ax3 = plt.subplot(1, 2, 2)
example_plot(ax1)example_plot(ax2)example_plot(ax3)plt.suptitle('Mixed nrows, ncols')
类似地,subplot2grid也有同样的限制,即行数和列数不能更改以使布局看起来良好。
·
·
·
·
·
·
·
·
·
·
·
·
fig = plt.figure(layout="constrained")
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)fig.suptitle('subplot2grid')
完整代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
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 = plt.figure(layout="constrained")
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)fig.suptitle('subplot2grid')
其他注意事项
● 约束布局只考虑刻度标签、轴标签、标题和图例。因此,其他艺术家(artist)可能被剪辑,也可能重叠。
● 它假设刻度标签、轴标签和标题所需的额外空间独立于图表域(axes)的原始位置。这通常是正确的,但极少数情况不是这样。
● 后端处理渲染字体的方式有一些小的差异,因此结果不会像数完全相同。
● 当艺术家使用超出图表域边界的图表域坐标,添加到图表域时将导致出现异常布局。这可以通过使用add_artist()将艺术家直接添加到图表(Figure)中来避免。有关示例,请参阅连接面片(ConnectionPatch)。
调试
约束的布局可能会以一些意想不到的方式失败。因为它使用约束解算器,解算器可以找到数学上正确的解,但这根本不是用户想要的。通常的失效模式是所有尺寸都塌陷到其最小允许值。如果发生这种情况,原因是下面两个之一:
1、没有足够的空间放置您请求绘制的元素。
2、存在一个错误(bug)-在这种情况下,在matplotlb/matplotlib#issues中打开一个问题。
如果存在错误,请使用不需要外部数据或依赖项(numpy除外)的独立示例进行报告。
关于算法的注释
约束的算法相对简单,但由于我们布局图表的方式复杂,因此具有一定的复杂性。
Matplotlib中的布局是通过GridSpec类使用gridspecs执行的。gridspecs将图表按逻辑划分为行和列,这些行和列中的图表域(Axes)的相对宽度由宽度比例(width_ratios)和高度比例(height_ratios)设置。
在约束布局中,每个gridsec都有一个与其关联的layoutgrid。layoutgrid有一系列left(左)、right(右)变量用于每列,top(上)和bottom(下)变量用于每行,此外,它还为左、右、上和下中的每一个留有一个边距。在每一行中,上/下边距都被加宽,直到该行中的所有装饰符都被容纳。列和左/右页边距也是如此。
set_position:
https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_position.html#matplotlib.axes.Axes.set_position
pyplot.subplot:
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplot.html#matplotlib.pyplot.subplot
GridSpec:
https://matplotlib.org/stable/api/_as_gen/matplotlib.gridspec.GridSpec.html#matplotlib.gridspec.GridSpec
subplot2grid:
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplot2grid.html#matplotlib.pyplot.subplot2grid
Figure:
https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure
add_artist():
https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.add_artist
ConnectionPatch:
https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.ConnectionPatch.html#matplotlib.patches.ConnectionPatch
matplotlib/matplotlib#issues:
https://github.com/matplotlib/matplotlib/issues