Limits of matplotlib's 'best' legend location for reproducible figures
0 reputation · 30 Jul 2020, 22:01 UTC
0 reputation · 30 Jul 2020, 22:01 UTC
Ensure that a legend placed with loc='best' remains in the same corner after interactive resizing or when saving to different file formats, so that figures are reproducible across environments.
The scoring algorithm that determines the 'best' location resides in the private _legend_box.py module and is not documented in the public API. As a result, its exact overlap metrics and decision thresholds are unknown, and the behavior may change between minor releases or differ with various backends (e.g., GTK, TkAgg, notebook). This opacity makes it impossible to guarantee deterministic placement without manually fixing the location.
Specific questions:
bbox_to_anchor to a fixed normalized coordinate guarantee identical legend positions across different figure sizes, backends, and output formats?35275 reputation · 31 Jul 2020, 00:49 UTC
Matplotlib’s internal loc='best' scorer (found in _legend_box._calc_best_location) evaluates each corner candidate by:
Because the scoring logic lives in a private module, there is no public method to retrieve or reproduce the score for a given axes configuration. Users cannot programmatically enforce the same placement without manually fixing the legend location.
Setting bbox_to_anchor to a fixed normalized coordinate (e.g., (1, 1) for the upper‑right corner) together with an explicit loc value (e.g., loc='upper right') guarantees that the legend’s relative position stays the same within the figure. However, the pixel position will change if any of the following vary:
tight_layout or constrained_layout, which resize the axes after the legend is placed.Therefore, to obtain identical legend positions across different runs, file formats, or environments you must control those variables.
plt.figure(figsize=(6, 4), dpi=100).plt.tight_layout(False) and plt.set_constrained_layout(False) (or simply avoid calling them).ax.legend(loc='upper right', bbox_to_anchor=(1, 1), borderaxespad=0.)
dpi if raster output is needed.fig.canvas.draw()
legend_bbox = legend.get_bbox_to_anchor().transformed(fig.transFigure.inverted())
print(legend_bbox)
Repeat the process with a different backend (e.g., switch from TkAgg to Agg) and confirm the printed bbox values match.If you are using tight_layout or constrained_layout in your script, the legend’s position may still shift because these layout managers resize the axes after the legend is placed. Let me know whether you employ either of these layout managers so I can advise on the exact steps to disable them.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.