如何以编程方式为 matplotlib colorbar 设置合适的填充?
How to programtically set a suitable padding for matplotlib colorbar?
我正在创建一些子图,每个子图在底部都有自己的颜色条。使用以下方法添加颜色条:
cax, kw = mcbar.make_axes_gridspec(ax, orientation='horizontal',
pad=pad,
fraction=0.07, shrink=0.85, aspect=35)
figure.colorbar(cs, cax=cax, orientation='horizontal')
调整了pad
参数,如果没有xticklabels,数值会变小,避免浪费space。
完整脚本:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colorbar as mcbar
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 50)
X, Y = np.meshgrid(x, y)
Z = X**2+np.sin(Y)
figure = plt.figure(figsize=(12, 10))
nrow = 3
ncol = 2
for ii in range(nrow*ncol):
ax = figure.add_subplot(nrow, ncol, ii+1)
row, col = np.unravel_index(ii, (nrow, ncol))
cs = ax.contourf(X, Y, Z)
if row == nrow-1:
# larger padding to make room for xticklabels
pad = 0.15
else:
# smaller padding otherwise
pad = 0.05
ax.tick_params(labelbottom=False)
if row == 1 and col == 1:
# add xlabel would need more padding
ax.set_xlabel('X')
cax, kw = mcbar.make_axes_gridspec(ax, orientation='horizontal',
pad=pad,
fraction=0.07, shrink=0.85, aspect=35)
figure.colorbar(cs, cax=cax, orientation='horizontal')
ax.set_title(str(ii+1))
figure.tight_layout()
figure.show()
输出图:
但是当前的解决方案是使用硬编码的填充值(0.15
如果有 xticklabels
,0.05
否则),并且它不能很好地适应 xlabels
(参见子图 4),或更改图形大小。
有没有办法以编程方式计算出合适的填充值来放置颜色条?如果没有 xlabels
或 xticklabels
,也许可以通过调整父轴对象的边界框使其 bbox 更小,或者通过找出父轴的坐标并以某种方式计算填充?
你可以通过比较整个轴和yaxis的边界框来得到刻度标签和轴标签所需的space。为了获得这些边界框,我们需要一个渲染器。要使其可用,我们首先需要绘制 canvas。边界框以显示坐标返回,因此我们使用倒轴变换将它们转换为轴坐标。它们的 y 坐标的差异给出了所需的额外填充:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colorbar as mcbar
from matplotlib.transforms import Bbox
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 50)
X, Y = np.meshgrid(x, y)
Z = X**2+np.sin(Y)
figure = plt.figure(figsize=(12, 10))
figure.canvas.draw() # to get renderer
nrow = 3
ncol = 2
for ii in range(nrow*ncol):
ax = figure.add_subplot(nrow, ncol, ii+1)
row, col = np.unravel_index(ii, (nrow, ncol))
cs = ax.contourf(X, Y, Z)
if row != nrow-1:
ax.tick_params(labelbottom=False)
if row == 1 and col == 1:
# add xlabel would need more padding
ax.set_xlabel('X')
# get height of ticklabels and label
b = ax.transAxes.inverted().transform(
[ax.yaxis.get_tightbbox(figure.canvas.renderer).p0,
ax.get_tightbbox(figure.canvas.renderer).p0]
)
pad = 0.05 + (b[0]-b[1])[1]
cax, kw = mcbar.make_axes_gridspec(ax, orientation='horizontal',
pad=pad,
fraction=0.07, shrink=0.85, aspect=35)
figure.colorbar(cs, cax=cax, orientation='horizontal')
ax.set_title(str(ii+1))
这个方案的缺点是3轴和4轴的高度不一样。您可以通过将一行中所有轴的 ymin
调整为行最大值来解决此问题:
figure.tight_layout()
for i in range(0, 2*ncol*nrow, 2*ncol):
ymin = 0
for j in range(0, 2*ncol, 2):
ymin = max(ymin, figure.axes[i+j].get_position().ymin)
for j in range(0, 2*ncol, 2):
b = figure.axes[i+j].get_position()
figure.axes[i+j].set_position(Bbox([[b.xmin,ymin],[b.xmax,b.ymax]]))
请注意,此调整 必须在应用 tight_layout
之前完成!
我正在创建一些子图,每个子图在底部都有自己的颜色条。使用以下方法添加颜色条:
cax, kw = mcbar.make_axes_gridspec(ax, orientation='horizontal',
pad=pad,
fraction=0.07, shrink=0.85, aspect=35)
figure.colorbar(cs, cax=cax, orientation='horizontal')
调整了pad
参数,如果没有xticklabels,数值会变小,避免浪费space。
完整脚本:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colorbar as mcbar
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 50)
X, Y = np.meshgrid(x, y)
Z = X**2+np.sin(Y)
figure = plt.figure(figsize=(12, 10))
nrow = 3
ncol = 2
for ii in range(nrow*ncol):
ax = figure.add_subplot(nrow, ncol, ii+1)
row, col = np.unravel_index(ii, (nrow, ncol))
cs = ax.contourf(X, Y, Z)
if row == nrow-1:
# larger padding to make room for xticklabels
pad = 0.15
else:
# smaller padding otherwise
pad = 0.05
ax.tick_params(labelbottom=False)
if row == 1 and col == 1:
# add xlabel would need more padding
ax.set_xlabel('X')
cax, kw = mcbar.make_axes_gridspec(ax, orientation='horizontal',
pad=pad,
fraction=0.07, shrink=0.85, aspect=35)
figure.colorbar(cs, cax=cax, orientation='horizontal')
ax.set_title(str(ii+1))
figure.tight_layout()
figure.show()
输出图:
但是当前的解决方案是使用硬编码的填充值(0.15
如果有 xticklabels
,0.05
否则),并且它不能很好地适应 xlabels
(参见子图 4),或更改图形大小。
有没有办法以编程方式计算出合适的填充值来放置颜色条?如果没有 xlabels
或 xticklabels
,也许可以通过调整父轴对象的边界框使其 bbox 更小,或者通过找出父轴的坐标并以某种方式计算填充?
你可以通过比较整个轴和yaxis的边界框来得到刻度标签和轴标签所需的space。为了获得这些边界框,我们需要一个渲染器。要使其可用,我们首先需要绘制 canvas。边界框以显示坐标返回,因此我们使用倒轴变换将它们转换为轴坐标。它们的 y 坐标的差异给出了所需的额外填充:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colorbar as mcbar
from matplotlib.transforms import Bbox
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 50)
X, Y = np.meshgrid(x, y)
Z = X**2+np.sin(Y)
figure = plt.figure(figsize=(12, 10))
figure.canvas.draw() # to get renderer
nrow = 3
ncol = 2
for ii in range(nrow*ncol):
ax = figure.add_subplot(nrow, ncol, ii+1)
row, col = np.unravel_index(ii, (nrow, ncol))
cs = ax.contourf(X, Y, Z)
if row != nrow-1:
ax.tick_params(labelbottom=False)
if row == 1 and col == 1:
# add xlabel would need more padding
ax.set_xlabel('X')
# get height of ticklabels and label
b = ax.transAxes.inverted().transform(
[ax.yaxis.get_tightbbox(figure.canvas.renderer).p0,
ax.get_tightbbox(figure.canvas.renderer).p0]
)
pad = 0.05 + (b[0]-b[1])[1]
cax, kw = mcbar.make_axes_gridspec(ax, orientation='horizontal',
pad=pad,
fraction=0.07, shrink=0.85, aspect=35)
figure.colorbar(cs, cax=cax, orientation='horizontal')
ax.set_title(str(ii+1))
这个方案的缺点是3轴和4轴的高度不一样。您可以通过将一行中所有轴的 ymin
调整为行最大值来解决此问题:
figure.tight_layout()
for i in range(0, 2*ncol*nrow, 2*ncol):
ymin = 0
for j in range(0, 2*ncol, 2):
ymin = max(ymin, figure.axes[i+j].get_position().ymin)
for j in range(0, 2*ncol, 2):
b = figure.axes[i+j].get_position()
figure.axes[i+j].set_position(Bbox([[b.xmin,ymin],[b.xmax,b.ymax]]))
请注意,此调整 必须在应用 tight_layout
之前完成!