4

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 Image without legend

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.

enter image description here

Is there a good way to retain the tight layout of the image and also retain the legend in the generated image?

Edit 1

Following the instruction in this post, 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')

Edit2

According to the Matplotlib developer, there seems to be a bug that legend produced by fig.legend method are not taken into account when we use tight layout.

jdhao
  • 24,001
  • 18
  • 134
  • 273

2 Answers2

3

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)
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
1

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')

enter image description here

Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75
  • 1
    Yes, I have also find that. Using `axes.legend()` method, I can save the image with the legend present without the `bbox_extra_artists` argument. – jdhao Jan 08 '18 at 00:58