loc='best' 并非智能算法,而是暴力遍历10种预设位置并选择与坐标轴范围重叠最小者,完全忽略图中线条、散点、文字等实际元素,因此在多子图、tight_layout、aspect设置或存在浮动文本时极易失效。

loc='best' 真的“最佳”吗?
loc='best' 并不是智能算法,而是暴力遍历 10 种预设位置('upper right'、'lower left' 等),选其中重叠面积最小的那个。它只考虑 legend 框与坐标轴范围(axes bbox)的重叠,**完全忽略图中实际绘制的线条、散点、文字等元素**。所以当你有密集折线、大 marker 或标题/标签靠边时,loc='best' 很可能把 legend 压在数据上,看似“自动”,实则不可靠。
为什么 loc='best' 经常失效?
常见失效场景包括:
- 多子图(
subplots)中,legend 只检测当前 axes,不感知其他子图区域 - 使用了
plt.tight_layout()或fig.subplots_adjust()后,axes 实际范围变化,但loc='best'仍按原始 bbox 计算 - 启用了
ax.set_aspect('equal')或设置了非默认anchor,导致 bbox 形变,重叠判断失准 - 图中存在
ax.text()、ax.annotate()等浮动元素,loc='best'对它们视而不见
比 loc='best' 更靠谱的替代方案
手动指定 + 微调仍是主流做法,但可借助工具减少试错:
- 用
ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1.0), borderaxespad=0)将 legend 放到图右侧外——这是最常用且稳定的布局,尤其适合横向长图 - 对单图,先调用
ax.legend()不指定loc,再执行plt.tight_layout(),最后用ax.get_legend().set_draggable(True)手动拖到满意位置(仅限交互环境如 Jupyter) - 需要自动化时,改用
matplotlib.pyplot.legend(bbox_to_anchor=(x, y), loc='center')配合fig.transFigure坐标系,例如bbox_to_anchor=(0.5, 0.02), loc='lower center'放底部居中,避开所有数据区
真正想“自动避让数据”,得换思路
Matplotlib 本身不提供数据感知型 legend 定位。若必须动态避让,需自行计算:
- 获取所有 artist 的 bounding box:
for line in ax.lines: print(line.get_window_extent())(需先 draw) - 用
ax.get_tightbbox(renderer)获取图中所有元素的总包围盒 - 在空白区域(如右上角 20% × 20% 区域)采样多个候选点,逐一测试是否与任何 artist bbox 相交
- 选相交最少的点,反算为
bbox_to_anchor值
这已超出 loc='best' 范畴,属于定制化布局逻辑,且 renderer 依赖后端,跨环境易出错。多数情况下,固定外置位置(如右侧或底部)比追求“自动”更省心也更稳定。










