["python","matplotlib","legend"]
Moving matplotlib legend outside of the axis makes it cutoff by the figure box

I'm familiar with the following questions: Matplotlib savefig with a legend outside the plot How to put the legend out of the plot It seems that the answers in these questions have the luxury of being able to fiddle with the exact shrinking of the axis so that the legend fits. Shrinking the axes, however, is not an ideal solution because it makes the data smaller making it actually more difficult to interpret; particularly when its complex and there are lots of things going on ... hence needing a large legend The example of a complex legend in the documentation demonstrates the need for this because the legend in their plot actually completely obscures multiple data points. http://matplotlib.sourceforge.net/users/legend_guide.html#legend-of-complex-plots What I would like to be able to do is dynamically expand the size of the figure box to accommodate the expanding figure legend. import matplotlib.pyplot as plt import numpy as np x = np.arange(-2*np.pi, 2*np.pi, 0.1) fig = plt.figure(1) ax = fig.add_subplot(111) ax.plot(x, np.sin(x), label='Sine') ax.plot(x, np.cos(x), label='Cosine') ax.plot(x, np.arctan(x), label='Inverse tan') lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,0)) ax.grid('on') Notice how the final label 'Inverse tan' is actually outside the figure box (and looks badly cutoff - not publication quality!) Finally, I've been told that this is normal behaviour in R and LaTeX, so I'm a little confused why this is so difficult in python... Is there a historical reason? Is Matlab equally poor on this matter? I have the (only slightly) longer version of this code on pastebin http://pastebin.com/grVjc007
[EDIT - 25th Feb 2025] My day job is no longer Python, so I'm not following the recent matplotlib developments. Please read all the newer answers here as there look to be some excellent modern suggestions compared to this solution from the ancient history of 2012. Sorry EMS, but I actually just got another response from the matplotlib mailling list (Thanks goes out to Benjamin Root). The code I am looking for is adjusting the savefig call to: fig.savefig('samplefigure', bbox_extra_artists=(lgd,), bbox_inches='tight') #Note that the bbox_extra_artists must be an iterable This is apparently similar to calling tight_layout, but instead you allow savefig to consider extra artists in the calculation. This did in fact resize the figure box as desired. import matplotlib.pyplot as plt import numpy as np plt.gcf().clear() x = np.arange(-2*np.pi, 2*np.pi, 0.1) fig = plt.figure(1) ax = fig.add_subplot(111) ax.plot(x, np.sin(x), label='Sine') ax.plot(x, np.cos(x), label='Cosine') ax.plot(x, np.arctan(x), label='Inverse tan') handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5,-0.1)) text = ax.text(-0.2,1.05, "Aribitrary text", transform=ax.transAxes) ax.set_title("Trigonometry") ax.grid('on') fig.savefig('samplefigure', bbox_extra_artists=(lgd,text), bbox_inches='tight') This produces: [edit] The intent of this question was to completely avoid the use of arbitrary coordinate placements of arbitrary text as was the traditional solution to these problems. Despite this, numerous edits recently have insisted on putting these in, often in ways that led to the code raising an error. I have now fixed the issues and tidied the arbitrary text to show how these are also considered within the bbox_extra_artists algorithm. [edit] Some of the comments below note that since 2019, the command has been simplified. plt.savefig('x.png', bbox_inches='tight') was sufficient. Thanks for sharing. – mateuszb Jun 27, 2019