如何在Matplotlib多子图图形中自动将图例(legend)适配到坐标轴(ax)内部?
让Matplotlib多子图的图例自动适配坐标轴尺寸
我明白你的需求——不想手动设置字体大小或图例尺寸,希望每个子图的图例能自动适配所在坐标轴的空间,不会超出边界。这里有个可行的方案,通过动态计算坐标轴和图例的尺寸来自动调整,代码实现起来也很直观:
核心思路
- 临时创建一个图例,获取它的初始尺寸(只有绘制画布后才能拿到准确的尺寸数据)
- 对比坐标轴的可用空间,计算出合适的缩放比例
- 根据缩放比例调整图例的字体大小,再创建最终的图例并放置在坐标轴内部
完整实现代码
import matplotlib.pyplot as plt import numpy as np def auto_adjust_legend(ax, legend_label): # 获取坐标轴的英寸尺寸(转换后适配当前DPI) ax_extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) ax_width, ax_height = ax_extent.width, ax_extent.height # 创建临时图例来获取初始尺寸 temp_legend = ax.legend([legend_label], [legend_label]) fig.canvas.draw() legend_extent = temp_legend.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) temp_legend.remove() # 删掉临时图例,避免干扰 # 计算缩放比例:让图例最多占坐标轴80%的空间,避免过于拥挤 scale_factor = min(ax_width / legend_extent.width * 0.8, ax_height / legend_extent.height * 0.8) # 基于Matplotlib默认字体大小调整新的字体尺寸 adjusted_font_size = plt.rcParams['legend.fontsize'] * scale_factor # 创建适配后的图例,放在右上角靠近边缘的位置,减少留白 ax.legend( fontsize=adjusted_font_size, loc='upper right', bbox_to_anchor=(0.97, 0.97), borderaxespad=0 # 消除图例和坐标轴之间的默认 padding ) # 示例数据 x = np.linspace(0, 2 * np.pi, 400) a = x ** 2 b = x ** 3 c = x ** 4 functions = [a, b, c] long_label = "This is a long label for a legend and I try to fit to its ax" # 创建2x2子图,用constrained_layout代替tight_layout,自动处理子图间距 fig, axs = plt.subplots(2, 2, constrained_layout=True) # 绘制曲线 for y in functions: axs[0, 0].plot(x, y, label=long_label) axs[0, 1].plot(x, -y, label=long_label) axs[1, 0].plot(x, y, label=long_label) axs[1, 1].plot(x, -y, label=long_label) # 配置每个子图并应用自动适配的图例 for ax in axs.flat: ax.set_title('Title') ax.set(xlabel='x-label', ylabel='y-label') auto_adjust_legend(ax, long_label) plt.show()
额外说明
- 用
constrained_layout=True代替tight_layout(),能更智能地处理标题、坐标轴标签和子图之间的间距,避免重叠问题 - 如果你觉得图例和坐标轴边缘太近,可以微调
bbox_to_anchor的数值(比如改成(0.95, 0.95))或者给borderaxespad设置一个小值(比如0.1) - 这个方法会根据每个子图的实际尺寸动态调整,不管子图布局怎么变,图例都能适配所在坐标轴的空间
内容的提问来源于stack exchange,提问作者allabur




