在后台回复【阅读书籍】
即可获取python相关电子书~
Hi,我是山月。
今天来给大家介绍下Matplotlib的布局部分~
01
自定义图形布局
可以创建axes的网格状组合的方法:
1)subplots()
用于创建图形和axes的主要函数。它类似于 matplotlib.pyplot.subplot(),但会同时创建并放置图形上的所有axes。
2)GridSpec
指定将放置子图的网格几何形状。要设置网格的行数和列数,也可调整子图的布局参数(如left、right等)。
3)SubplotSpec
在给定的 GridSpec 中指定子图的位置。
4)subplot2grid()
是与 subplot() 类似的辅助函数,但使用从 0 开始的索引并让 subplot 占据多个单元格。
使用 subplots() 创建一个 2×2 网格非常简单,它会返回一个 Figure 实例和一个 Axes 对象数组。
import matplotlib.pyplot as plt
fig1, f1_axes = plt.subplots(ncols=2, nrows=2, constrained_layout=True)
plt.show()
效果:
使用gridspec 实现相同的效果会过于冗长:
先分别创建figure和GridSpec实例,然后将 gridspec 实例的元素传递给 add_subplot() 方法以创建轴对象。
gridspec元素的访问方式与 numpy 数组相同。
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig2 = plt.figure(constrained_layout=True)
spec2 = gridspec.GridSpec(ncols=2, nrows=2, figure=fig2)
f2_ax1 = fig2.add_subplot(spec2[0, 0])
f2_ax2 = fig2.add_subplot(spec2[0, 1])
f2_ax3 = fig2.add_subplot(spec2[1, 0])
f2_ax4 = fig2.add_subplot(spec2[1, 1])
plt.show()
gridspec 的强大之处在于能够创建跨行和列的子图。注意用于选择每个子图将占据的gridspec部分的 Numpy 切片语法。
我们也可以使用 Figure.add_gridspec方法创建不同宽度的子图:
import matplotlib.pyplot as plt
fig3 = plt.figure(constrained_layout=True)
gs = fig3.add_gridspec(3, 3)
f3_ax1 = fig3.add_subplot(gs[0, :])
f3_ax1.set_title('gs[0, :]')
f3_ax2 = fig3.add_subplot(gs[1, :-1])
f3_ax2.set_title('gs[1, :-1]')
f3_ax3 = fig3.add_subplot(gs[1:, -1])
f3_ax3.set_title('gs[1:, -1]')
f3_ax4 = fig3.add_subplot(gs[-1, 0])
f3_ax4.set_title('gs[-1, 0]')
f3_ax5 = fig3.add_subplot(gs[-1, -2])
f3_ax5.set_title('gs[-1, -2]')
plt.show()
效果:
这个方法与上面的方法类似,先初始化一个统一的gridspec,然后使用 numpy 索引和切片为给定的子图分配多个“单元格”。
import matplotlib.pyplot as plt
fig4 = plt.figure(constrained_layout=True)
spec4 = fig4.add_gridspec(ncols=2, nrows=2)
anno_opts = dict(xy=(0.5, 0.5), xycoords='axes fraction',
va='center', ha='center')
f4_ax1 = fig4.add_subplot(spec4[0, 0])
f4_ax1.annotate('GridSpec[0, 0]', **anno_opts)
fig4.add_subplot(spec4[0, 1]).annotate('GridSpec[0, 1:]', **anno_opts)
fig4.add_subplot(spec4[1, 0]).annotate('GridSpec[1:, 0]', **anno_opts)
fig4.add_subplot(spec4[1, 1]).annotate('GridSpec[1:, 1:]', **anno_opts)
plt.show()
效果:
另一种方法是使用 width_ratios 和 height_ratios 参数,这些关键字参数是数字列表。
请注意,绝对值是没有意义的,只有它们的相对比率很重要。
这意味着在同样宽的图中width_ratios=[2, 4, 8] 等效于 width_ratios=[1, 2, 4]。
import matplotlib.pyplot as plt
fig5 = plt.figure(constrained_layout=True)
widths = [2, 3, 1.5]
heights = [1, 3, 2]
spec5 = fig5.add_gridspec(ncols=3, nrows=3, width_ratios=widths,
height_ratios=heights)
for row in range(3):
for col in range(3):
ax = fig5.add_subplot(spec5[row, col])
label = 'Width: {}\nHeight: {}'.format(widths[col], heights[row])
ax.annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
plt.show()
效果:
学习使用 width_ratios 和 height_ratios 特别有用,因为顶级函数 subplots() 在 gridspec_kw 参数中接受它们。
就此而言,GridSpec 接受的任何参数都可以通过 gridspec_kw 参数传递给 subplots()。
在不直接使用 gridspec 实例的情况下重新创建上一个图:
import matplotlib.pyplot as plt
widths = [2, 3, 1.5]
heights = [1, 3, 2]
gs_kw = dict(width_ratios=widths, height_ratios=heights)
fig6, f6_axes = plt.subplots(ncols=3, nrows=3, constrained_layout=True,
gridspec_kw=gs_kw)
for r, row in enumerate(f6_axes):
for c, ax in enumerate(row):
label = 'Width: {}\nHeight: {}'.format(widths[c], heights[r])
ax.annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
plt.show()
subplots 和 get_gridspec 方法可以组合使用,因为有时使用 subplots 制作子图更方便。
import matplotlib.pyplot as plt
fig7, f7_axs = plt.subplots(ncols=3, nrows=3)
gs = f7_axs[1, 2].get_gridspec()
# 移除下面的轴
for ax in f7_axs[1:, -1]:
ax.remove()
axbig = fig7.add_subplot(gs[1:, -1])
axbig.annotate('Big Axes \nGridSpec[1:, -1]', (0.1, 0.5),
xycoords='axes fraction', va='center')
fig7.tight_layout()
plt.show()
效果:
当使用 GridSpec 时,你可以调整用 GridSpec 创建的子图的布局参数。
请注意,此选项与 constrained_layout 或 Figure.tight_layout 不兼容。
import matplotlib.pyplot as plt
fig8 = plt.figure(constrained_layout=False)
gs1 = fig8.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.48, wspace=0.05)
f8_ax1 = fig8.add_subplot(gs1[:-1, :])
f8_ax2 = fig8.add_subplot(gs1[-1, :-1])
f8_ax3 = fig8.add_subplot(gs1[-1, -1])
plt.show()
效果:
这类似于 subplots_adjust(),但它只影响从给定的 GridSpec 创建的子图。
import matplotlib.pyplot as plt
fig9 = plt.figure(constrained_layout=False)
gs1 = fig9.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.48,
wspace=0.05)
f9_ax1 = fig9.add_subplot(gs1[:-1, :])
f9_ax2 = fig9.add_subplot(gs1[-1, :-1])
f9_ax3 = fig9.add_subplot(gs1[-1, -1])
gs2 = fig9.add_gridspec(nrows=3, ncols=3, left=0.55, right=0.98,
hspace=0.05)
f9_ax4 = fig9.add_subplot(gs2[:, :-1])
f9_ax5 = fig9.add_subplot(gs2[:-1, -1])
f9_ax6 = fig9.add_subplot(gs2[-1, -1])
plt.show()
效果:
你可以从 SubplotSpec 创建 GridSpec,在这种情况下,其布局参数设置为给定的 SubplotSpec 的位置。
import matplotlib.pyplot as plt
fig10 = plt.figure(constrained_layout=True)
gs0 = fig10.add_gridspec(1, 2)
gs00 = gs0[0].subgridspec(2, 3)
gs01 = gs0[1].subgridspec(3, 2)
for a in range(2):
for b in range(3):
fig10.add_subplot(gs00[a, b])
fig10.add_subplot(gs01[b, a])
plt.show()
效果:
一个更复杂的嵌套 GridSpec 示例:
import matplotlib.pyplot as plt
import numpy as np
from itertools import product
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
fig11 = plt.figure(figsize=(8, 8), constrained_layout=False)
# gridspec内部的gridspec
outer_grid = fig11.add_gridspec(4, 4, wspace=0.0, hspace=0.0)
for i in range(16):
inner_grid = outer_grid[i].subgridspec(3, 3, wspace=0.0, hspace=0.0)
a, b = int(i/4)+1, i % 4+1
for j, (c, d) in enumerate(product(range(1, 4), repeat=2)):
ax = fig11.add_subplot(inner_grid[j])
ax.plot(*squiggle_xy(a, b, c, d))
ax.set_xticks([])
ax.set_yticks([])
fig11.add_subplot(ax)
all_axes = fig11.get_axes()
# 只露出外面的边框线
for ax in all_axes:
for sp in ax.spines.values():
sp.set_visible(False)
if ax.get_subplotspec().is_first_row():
ax.spines['top'].set_visible(True)
if ax.get_subplotspec().is_last_row():
ax.spines['bottom'].set_visible(True)
if ax.get_subplotspec().is_first_col():
ax.spines['left'].set_visible(True)
if ax.get_subplotspec().is_last_col():
ax.spines['right'].set_visible(True)
plt.show()
效果:
02
约束布局
constrained_layout 可以自动调整子图和装饰(如图例和颜色条),以便它们适合图形窗口,同时仍尽可能保留用户请求的逻辑布局。
在将axes添加到图形之前,需要激活 constrained_layout,有两种方法激活:
1)使用 subplots() 或 figure() 的相应参数:
plt.subplots(constrained_layout=True)
2)通过 rcParams 激活:
plt.rcParams['figure.constrained_layout.use'] = True
注意事项 :
constrained_layout 只考虑刻度标签、轴标签、标题和图例。因此,其他artists可能被剪裁,也可能重叠。
它假定刻度标签、轴标签和标题所需的额外空间与轴的原始位置无关。这通常是正确的,但在极少数情况下并非如此。
后端处理渲染字体的方式存在细微差别,因此结果不会是完全相同的。
artists添加超出axes 边界的坐标轴时会产生不一样的布局,这可以通过使用 add_artist() 将artists直接添加到图形中来避免。
在 Matplotlib 中,axes(包括子图)的位置在标准化图形坐标中指定。
但是轴标签或标题(有时甚至是刻度标签)可能会超出图形区域,从而被剪裁。
import matplotlib.pyplot as plt
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
fig, ax = plt.subplots(constrained_layout=False)
example_plot(ax, fontsize=24)
plt.show()
效果:
为了防止这种情况,可以使用 constrained_layout=True 来自动调整图形。
fig, ax = plt.subplots(constrained_layout=True)
example_plot(ax, fontsize=24)
效果:
当你有多个子图时,你可能会看到不同轴的标签相互重叠。
import matplotlib.pyplot as plt
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
fig, axs = plt.subplots(2, 2, constrained_layout=False)
for ax in axs.flat:
example_plot(ax)
plt.show()
效果:
在对plt.subplots的调用中指定 constrained_layout=True 会导致布局受到适当的约束。
fig, axs = plt.subplots(2, 2, constrained_layout=True)
for ax in axs.flat:
example_plot(ax)
效果:
如果你要使用 colorbar() 命令创建颜色条,则需要为其腾出空间,可以通过constrained_layout 自动执行此操作。
注意,如果你指定 use_gridspec=True,它将被忽略,因为此选项用于通过紧凑布局。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
# 如上所述:这使得所有pcolormesh调用一致:
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
fig, ax = plt.subplots(figsize=(4, 4), constrained_layout=True)
im = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(im, ax=ax, shrink=0.6)
plt.show()
效果:
如果你为颜色条的 ax 参数指定axes列表(或其他可迭代容器),则 constrained_layout 将占用指定axes的空间。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
fig, axs = plt.subplots(2, 2, figsize=(4, 4), constrained_layout=True)
for ax in axs.flat:
im = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(im, ax=axs, shrink=0.6)
plt.show()
效果:
如果你从axes网格中指定axes列表,颜色条将适当地占用空间并留下间隙,但所有子图仍将具有相同的大小。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
fig, axs = plt.subplots(3, 3, figsize=(4, 4), constrained_layout=True)
for ax in axs.flat:
im = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(im, ax=axs[1:, ][:, 1], shrink=0.8)
fig.colorbar(im, ax=axs[:, -1], shrink=0.6)
plt.show()
效果:
颜色条不会如理想中一样是对齐的,因为与底部轴配对的颜色条被绑定到轴的subplotspec 。
因此当gridspec级别的颜色条被添加时,颜色条会收缩。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
fig, axs = plt.subplots(3, 1, figsize=(4, 4), constrained_layout=True)
for ax in axs[:2]:
im = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(im, ax=axs[:2], shrink=0.6)
im = axs[2].pcolormesh(arr, **pc_kwargs)
fig.colorbar(im, ax=axs[2], shrink=0.6)
plt.show()
效果:
constrained_layout 也可以为标题腾出空间。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
fig, axs = plt.subplots(2, 2, figsize=(4, 4), constrained_layout=True)
for ax in axs.flat:
im = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(im, ax=axs, shrink=0.6)
fig.suptitle('Big Suptitle')
plt.show()
效果:
图例可以放置在其父轴之外。
约束布局可以为 Axes.legend() 创建的图例处理此问题,但不处理通过 Figure.legend() 创建的图例。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(constrained_layout=True)
ax.plot(np.arange(10), label='This is a plot')
ax.legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
plt.show()
效果:
其实这将从子图布局中占取空间:
import matplotlib.pyplot as plt
import numpy as np
fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
axs[0].plot(np.arange(10))
axs[1].plot(np.arange(10), label='This is a plot')
axs[1].legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
plt.show()
效果:
为了不让图例或其他artist 从子图布局中占取空间,我们可以使用leg.set_in_layout(False)。
当然,这意味着图例可能会被裁剪,但可以通过调用 fig.savefig('outname.png', bbox_inches='tight') 来保存完整的图。
注意,必须再次切换图例的 get_in_layout 状态才能使保存的文件正常工作。
并且如果我们希望在打印之前调整axes的大小,必须手动触发constrained_layout。
import matplotlib.pyplot as plt
import numpy as np
fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
axs[0].plot(np.arange(10))
axs[1].plot(np.arange(10), label='This is a plot')
leg = axs[1].legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
leg.set_in_layout(False)
fig.canvas.draw()
leg.set_in_layout(True)
fig.set_constrained_layout(False)
fig.savefig('CL01.png', bbox_inches='tight', dpi=100)
plt.show()
效果:
保存的文件如下所示:
其实更好的方法是使用 Figure.legend 提供的 legend 方法:
import matplotlib.pyplot as plt
import numpy as np
fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
axs[0].plot(np.arange(10))
lines = axs[1].plot(np.arange(10), label='This is a plot')
labels = [l.get_label() for l in lines]
leg = fig.legend(lines, labels, loc='center left',
bbox_to_anchor=(0.8, 0.5), bbox_transform=axs[1].transAxes)
fig.savefig('CL02.png', bbox_inches='tight', dpi=100)
plt.show()
对于 constrained_layout,我们在每个轴的边缘会设置一个填充。
此填充设置了距绘图边缘的距离,以及相邻绘图之间的最小距离。
它由函数 set_constrained_layout_pads 的关键字参数 w_pad 和 h_pad 以英寸为单位指定:
import matplotlib.pyplot as plt
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
fig, axs = plt.subplots(2, 2, constrained_layout=True)
for ax in axs.flat:
example_plot(ax, nodec=True)
ax.set_xticklabels('')
ax.set_yticklabels('')
fig.set_constrained_layout_pads(w_pad=4./72., h_pad=4./72.,
hspace=0., wspace=0.)
fig.suptitle('4.72')
fig, axs = plt.subplots(2, 2, constrained_layout=True)
for ax in axs.flat:
example_plot(ax, nodec=True)
ax.set_xticklabels('')
ax.set_yticklabels('')
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
hspace=0., wspace=0.)
fig.suptitle('2.72')
plt.show()
效果:
子图之间的间距由 wspace 和 hspace 设置,指定为子图组整体大小的一部分。
如果图形的大小发生变化,那么这些空间也会按比例变化。
fig, axs = plt.subplots(2, 2, constrained_layout=True)
for ax in axs.flat:
example_plot(ax, nodec=True)
ax.set_xticklabels('')
ax.set_yticklabels('')
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
hspace=0.2, wspace=0.2)
效果:
颜色条和其他子图的wspace、hsapce是分开放置的。
颜色条和它所连接的轴之间的填充永远不会小于 w_pad (对于垂直颜色条)或 h_pad (对于水平颜色条)。
请注意在 colorbar 调用中参数pad 默认的大小为0.02。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
fig, axs = plt.subplots(2, 2, constrained_layout=True)
for ax in axs.flat:
pc = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(pc, ax=ax, shrink=0.6, pad=0)
ax.set_xticklabels('')
ax.set_yticklabels('')
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
hspace=0.2, wspace=0.2)
plt.show()
效果:
在上面的示例中,颜色条与绘图的距离永远不会超过 2 pts,但如果我们希望它离图更远一点,我们可以将它的 pad 值指定为非零。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
fig, axs = plt.subplots(2, 2, constrained_layout=True)
for ax in axs.flat:
pc = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(pc, ax=ax, shrink=0.6, pad=0.05)
ax.set_xticklabels('')
ax.set_yticklabels('')
fig.set_constrained_layout_pads(w_pad=2./72., h_pad=2./72.,
hspace=0.2, wspace=0.2)
plt.show()
效果:
可以在脚本或 matplotlibrc 文件中设置五个参数,它们都有前缀 figure.constrained_layout:
use:是否使用 constrained_layout,默认为False。
w_pad、h_pad:围绕axes对象填充。值为表示英寸的浮点数,默认值为 3./72. 英寸(3 pts)
wspace,hspace:子图之间的空间。是一个浮点数表示被分隔的子图宽度的一小部分,默认值为 0.02。
import matplotlib.pyplot as plt
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
plt.rcParams['figure.constrained_layout.use'] = True
fig, axs = plt.subplots(2, 2, figsize=(3, 3))
for ax in axs.flat:
example_plot(ax)
plt.show()
效果:
除subplots() 之外,constrained_layout还可适用于GridSpec() 、 add_subplot() 方法。
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
plt.rcParams['figure.constrained_layout.use'] = True
fig = plt.figure()
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)
plt.show()
效果:
使用 add_gridspec 和 subgridspec函数可以完成更复杂的 gridspec 布局。
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
plt.rcParams['figure.constrained_layout.use'] = True
fig = plt.figure()
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)
plt.show()
效果:
如果我们希望两个网格的顶部和底部对齐,那么它们需要在同一个 gridspec 中:
import matplotlib.pyplot as plt
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
fig = plt.figure(constrained_layout=True)
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)
ax = fig.add_subplot(gs0[2:4, 1])
example_plot(ax)
ax = fig.add_subplot(gs0[4:, 1])
example_plot(ax)
plt.show()
效果:
此示例使用两个 gridspecs 使颜色条只和一组pcolors有关。
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
arr = np.arange(100).reshape((10, 10))
norm = mcolors.Normalize(vmin=0., vmax=100.)
pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
def docomplicated(suptitle=None):
fig = plt.figure(constrained_layout=True)
gs0 = fig.add_gridspec(1, 2, figure=fig, width_ratios=[1., 2.])
gsl = gs0[0].subgridspec(2, 1)
gsr = gs0[1].subgridspec(2, 2)
for gs in gsl:
ax = fig.add_subplot(gs)
example_plot(ax)
axs = []
for gs in gsr:
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.colorbar(pcm, ax=axs)
if suptitle is not None:
fig.suptitle(suptitle)
docomplicated()
plt.show()
效果:
手动调用set_position可以设置axes位置,因此 constrained_layout 不再对其产生影响。
import matplotlib.pyplot as plt
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
fig, axs = plt.subplots(1, 2)
example_plot(axs[0], fontsize=12)
axs[1].set_position([0.2, 0.2, 0.4, 0.4])
plt.show()
效果:
如果你想在数据空间中插入axes,则需要使用 fig.execute_constrained_layout() 来手动布局,然后在正确的位置里插入图形。
但是,如果之后更改图形的大小,它将无法正确定位。
同样,如果将图形打印到另一个后端,由于后端渲染字体的方式存在微小差异,位置也可能会略有变化。
import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox
plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
def example_plot(ax, fontsize=12, nodec=False):
ax.plot([1, 2])
ax.locator_params(nbins=3)
if not nodec:
ax.set_xlabel('x-label', fontsize=fontsize)
ax.set_ylabel('y-label', fontsize=fontsize)
ax.set_title('Title', fontsize=fontsize)
else:
ax.set_xticklabels('')
ax.set_yticklabels('')
fig, axs = plt.subplots(1, 2)
example_plot(axs[0], fontsize=12)
fig.execute_constrained_layout()
bb_data_ax2 = Bbox.from_bounds(0.5, 1., 0.2, 0.4)
disp_coords = axs[0].transData.transform(bb_data_ax2)
fig_coords_ax2 = fig.transFigure.inverted().transform(disp_coords)
bb_ax2 = Bbox(fig_coords_ax2)
ax2 = fig.add_axes(bb_ax2)
plt.show()
效果:
03
紧凑布局
tight_layout 会自动调整 subplot 参数,以便 subplot(s) 适合图形区域。
但它只检查刻度标签、轴标签和标题的范围。
在 matplotlib 中,axes(包括子图)的位置在图形坐标中指定。
轴标签或标题(有时甚至是刻度标签)可能会超出图形区域,从而被剪裁。
import matplotlib.pyplot as plt
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)
plt.show()
效果:
为了防止这种情况,需要调整 axes的位置。
可以使用tight_layout(),它会自动为你执行此操作:
fig, ax = plt.subplots()
example_plot(ax, fontsize=24)
plt.tight_layout()
效果:
注意, matplotlib.pyplot.tight_layout() 只会在调用时才调整子图参数。
为了在每次重绘图形时执行此调整,你可以调用 fig.set_tight_layout(True),或者等效地将 figure.autolayout 参数设置为 True。
当你有多个子图时,你通常会看到不同轴的标签相互重叠:
import matplotlib.pyplot as plt
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, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
example_plot(ax4)
plt.show()
效果:
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()
效果:
tight_layout() 可以接受 pad、w_pad 和 h_pad 的关键字参数,以控制图形边界周围和子图之间的填充。
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() 也将起作用。
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()
效果:
plt.tight_layout()也适用于使用 subplot2grid() 创建的子图:
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()
效果:
tight_layout()对于GridSpec方法也适用:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
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 = 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)
plt.show()
效果:
你可以提供一个可选的 rect 参数,它将指定子图的边界框。 rect 参数的值是标准图形坐标,默认为 (0, 0, 1, 1)。
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
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 = 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])
plt.show()
效果:
tight_layout()也可用于具有多个 gridspecs 方法的图形:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
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 = 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])
gs2 = gridspec.GridSpec(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)
gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.5)
# 匹配两个网格的顶部和底部
top = min(gs1.top, gs2.top)
bottom = max(gs1.bottom, gs2.bottom)
gs1.update(top=top, bottom=bottom)
gs2.update(top=top, bottom=bottom)
plt.show()
效果:
添加图例:
import matplotlib.pyplot as plt
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()
效果:
设置 leg.set_in_layout(False) 可以从边界框计算中删除图例:
import matplotlib.pyplot as plt
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()
效果:
虽然作用有限,但tight_layout()也支持 mpl_toolkits.axes_grid1:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import Grid
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 = 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()
plt.show()
效果:
如果你使用 colorbar() 命令创建颜色条,则创建的颜色条是 Axes 的实例,而不是 Subplot,因此tight_layout 不起作用。
但你可以通过 gridspec 创建一个颜色条作为子图。
import matplotlib.pyplot as plt
import numpy as np
arr = np.arange(100).reshape((10, 10))
fig = plt.figure(figsize=(4, 4))
im = plt.imshow(arr, interpolation="none")
plt.colorbar(im, use_gridspec=True)
plt.tight_layout()
plt.show()
效果:
另一种选择是使用 AxesGrid1 工具包为颜色条显式创建 axes。
import matplotlib.pyplot as plt
import numpy as np
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()
plt.show()
效果:
好啦,这个部分就介绍到这~
END
您的“点赞”、“在看”和 “分享”是我们产出的动力。