
Question:
I am trying to produce an image where I put the legend outside of the axes. But I find that if I use bbox_inches='tight'
in the plt.savefig()
method, the generated image does not contain the legend. A minimal working example to illustrate is as follows:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
x = np.arange(-5, 5, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)
fig, ax1= plt.subplots(ncols=1, nrows=1, figsize=(10, 6))
ax1.plot(x, y1, label='sin(x)')
ax1.plot(x, y2, label='cos(x)')
handles, labels = ax1.get_legend_handles_labels()
plt.figlegend(handles, labels, loc='upper left', ncol=2, frameon=False,
bbox_to_anchor=(0.11, 0.95))
plt.savefig('test.jpg', bbox_inches='tight')
The produced test.jpg
is shown below
<a href="https://i.stack.imgur.com/6ZVPs.jpg" rel="nofollow"><img alt="Image without legend" class="b-lazy" data-src="https://i.stack.imgur.com/6ZVPs.jpg" data-original="https://i.stack.imgur.com/6ZVPs.jpg" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>
If I remove bbox_inches='tight'
in savefig()
method.(shown below), the legend is present in the produced image , but there are two much white space in the four side of the image.
<a href="https://i.stack.imgur.com/uhHqw.jpg" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/uhHqw.jpg" data-original="https://i.stack.imgur.com/uhHqw.jpg" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>
Is there a good way to retain the tight layout of the image and also retain the legend in the generated image?
<h3>Edit 1</h3>Following the instruction in <a href="https://stackoverflow.com/questions/10101700/moving-matplotlib-legend-outside-of-the-axis-makes-it-cutoff-by-the-figure-box" rel="nofollow">this post</a>, I also tried to use bbox_extra_artists
argument in the savefig()
method, something like this
legend = plt.figlegend(handles, labels, loc='lower left', ncol=2, frameon=True,
bbox_to_anchor=(0.12, 0.88))
plt.savefig('test.jpg', bbox_extra_artists=(legend,), bbox_inches='tight')
As is pointed by @Diziet Asahi and @mportanceOfBeingErnest, if we use ax.legend()
method, everything works fine. The following code works,
legend = ax1.legend(handles, labels, ncol=2, frameon=False,
loc='lower left', bbox_to_anchor=(-0.01, 1.2))
plt.savefig('test.jpg', bbox_inches='tight')
<h3>Edit2</h3>
<a href="https://github.com/matplotlib/matplotlib/issues/10194" rel="nofollow">According to the Matplotlib developer</a>, there seems to be a bug that legend produced by fig.legend
method are not taken into account when we use tight layout.
I can't tell exactly why it happens (seems like a bug to me), but it looks like the problem is with your use of the top-level plt.figlegend()
function. The problem persist if using Figure.legend()
, but disappears if you replace it with Axes.legend()
:
legend = ax1.legend(handles, labels, loc='lower left', ncol=2, frameon=False,
bbox_to_anchor=(0,1.2))
fig.savefig('test.jpg', bbox_extra_artists=[legend], bbox_inches='tight')
<a href="https://i.stack.imgur.com/rSlq5.jpg" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/rSlq5.jpg" data-original="https://i.stack.imgur.com/rSlq5.jpg" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>
Answer2:You may create the legend using the .legend()
method of one of the figure's axes. In order to specify the legend coordinates in figure coordinates, as would be done using figlegend
you may use the bbox_transform
argument.
ax1.legend(handles, labels, loc='upper left', ncol=2, frameon=False,
bbox_to_anchor=(0.11, 0.95), bbox_transform=fig.transFigure)