1. 问题背景
用python
绘制下面这种图的时候,一般用xlable
作为子图的标题,这是因为plt.title()
方法绘制的标题在图的上方,与latex默认子图的标题不同。那么,在这种情况下,duplication ratio (%)
该如何显示呢?
一个最简单的方案,在上图中,使用plt.text()
绘制文字,利用labelpad
把xlable向下移动。
plt.text(0.5, -0.27, "duplication ratio (%)", ..., fontsize = 6, transform=subfig.transAxes)
plt.xlable("(a) ...", lablepad = 8, fontsize = 8)
可是这种方法需要不断调整plt.text
传入的参数,使得text
在xticks
与xlable
中间(不然很丑),非常之麻烦。
2. 基本思想(可以不看)
可以计算xlabel
的上坐标以及xticks
的下坐标,然后就可自动计算二者中心坐标,就可以自动居中绘制text
了。
3. 方法封装
首先封装一个方法,其中exp
代表xticks
的解释(例如:duplication ratio (%)
),title
通过xlable
展示出来
def add_explanation_and_title(fig, ax: Axes, exp, title, title_pad=10, title_fontsize=8, exp_fontsize=6):
lable_pad = title_pad
ax.set_xlabel(title, fontsize=title_fontsize, labelpad=lable_pad)
return [exp, title, title_pad, title_fontsize, exp_fontsize]
接着写一个关键函数用来计算xlable
和xticks
的距离,获取相应距离的函数主要通过get_window_extent()
方法获取
def adjust_explanation(fig, axes, context):
exp, title, title_pad, title_fontsize, exp_fontsize = context
fig.savefig("./temp.pdf", bbox_inches='tight', pad_inches=0)
midpoint = (0.5, 0.5)
for idx, ax in enumerate(axes):
xtick_label = ax.get_xticklabels()[0]
label_bbox1 = xtick_label.get_window_extent()
# print(label_bbox1)
figure_pos1 = label_bbox1.transformed(ax.transAxes.inverted())
# Get the xlabel object
xlable = ax.xaxis.label
label_bbox2 = xlable.get_window_extent()
# print(label_bbox2)
figure_pos2 = label_bbox2.transformed(ax.transAxes.inverted())
midpoint = (0.5, (figure_pos1.y0 + figure_pos2.y1) / 2 + 0.08)
print(midpoint)
for idx, ax in enumerate(axes):
ax.text(midpoint[0], midpoint[1], exp, ha='center', fontsize=exp_fontsize, transform=ax.transAxes)
在上述代码中,fig.savefig
非常重要,这是对当前画布做渲染绘制,得到相应的renderer
。renderer
用于为get_window_extent()
提供参考。
考虑这样一种情况,我们会用
plt.tight_layout()
来紧缩画布,此时调用savefig
便可以拿到绘制紧缩画布的renderer
4. 调用实例
fig = plt.figure(dpi = 300, figsize = (400, 500))
...
ax = fig.gca()
...
context = add_explanation_and_title(fig, ax, "duplication ratio (%)", "(a) Single Thread xxx")
...
# context保存了相关信息,用于后续adjust_explanation做参考
context = add_explanation_and_title(fig, ax, "duplication ratio (%)", "(b) Multi Thread xxx")
...
plt.tight_layout()
# 放在plt.tight_layout()后,通过savefig获取画布renderer,并
# 调整exp的最终坐标予以绘制
adjust_explanation(fig, fig.axes, context)
plt.savefig("xxx.pdf")
5. 总结
其实很难想象Matplotlib一直没有一个类似的库来支持以上操作。现有的,将title放到下面的方法只有类似如下方法:
plt.title('Scatter plot pythonspot.com', y=-0.01)
但这种方法导致对坐标的解释文字无法很好适配。另一种方案,即背景中的方案,也需要繁琐的适配。本文给出了一种自动化方案,希望能对科研作图有所帮助。
6. 起飞
OK,到这里就可以起飞了🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫🛫