Legend outside the plot

Hi everyone. I'm a newbye to matplotlib, so excuse my naive questions. I have a large experience with gnuplot and asymptote, and I only recently started to experiment with matplotlib.

Some background: I'm trying to use matplotlib mostly for complex plots with a lot of data. Gnuplot is usually fine, but I ended up too often producing huge batch scripts consisting of overlarge plot[] command sequences and pre-processed input files. Asymptote is awesome, but reading data is a mayor pita. As a result, I mostly use gnuplot interactively, and asymptote when I need to plot functions. Both packages are generally excellent, they have good support of multiple plots and usually never require manual placement of figure elements.

So far my experience with matplotlib is that a lot of manual placement seem to be required when coming down to multiple plots and legends.

I have a large figure, consisting of several dense plots.

I'm trying to plot the legend outside of the plots, but I find it bothersome how difficult it is to place the legend outside the plot.

With gnuplot, it's as simple as:

set key outside top right

This also works perfectly fine with 'multiplot' (gnuplot automatic multiple plot layout).
With matplotlib, I have to do the following:

legend(bbox_to_anchor=(1, 1 + ?), loc=2)

but how do I calculate the vertical location? Do I have to go at random to align the legend to the plot axis? It also breaks wonderfully with multiple plots, since plot(xyz) seems to consider only the axis and nothing more.

Can't we just have:

legend(loc=2, outside=true)

please? The vast majority of plots I do are too dense to have a legend inside the plot.

Also, related to my previous message (savefig bbox_inches='tight' does not consider suptitle), the legend is not considered when constructing a 'tight' boundary box.
It seems to me that the legend is another obvious element that should be taken into account without resorting to bbox_extra_artists.

Thanks again.

Matplotlib is designed to give you maximum control over the figure elements while still maintaining sensible defaults. This is helpful in some cases, and not so helpful in others. In your case of placing a legend outside an axes, calculating the location can be a bit of a trial-and-error, but if the number of elements in your legend is going to be fixed, once you find a good value, you can just leave it that way in your script.

Automatic determination of legend size is difficult because the size of the text objects are not known until draw time. However, it is possible to query your legend object for its bbox and then reposition your legend object accordingly (but only after the initial draw). This is tricky, but do-able.

By the way, another possible command you might want is called “figlegend”, which is a legend that is attached to the figure object rather than the axes object. It is plotted outside the axes, and has to be given all the objects to list, but it might be what you want. See this example:

http://matplotlib.sourceforge.net/examples/pylab_examples/figlegend_demo.html

I hope that is helpful,
Ben Root

···

On Mon, Mar 7, 2011 at 5:22 AM, Yuri D’Elia <wavexx@…878…867…> wrote:

Hi everyone. I’m a newbye to matplotlib, so excuse my naive questions. I have a large experience with gnuplot and asymptote, and I only recently started to experiment with matplotlib.

Some background: I’m trying to use matplotlib mostly for complex plots with a lot of data. Gnuplot is usually fine, but I ended up too often producing huge batch scripts consisting of overlarge plot[] command sequences and pre-processed input files. Asymptote is awesome, but reading data is a mayor pita. As a result, I mostly use gnuplot interactively, and asymptote when I need to plot functions. Both packages are generally excellent, they have good support of multiple plots and usually never require manual placement of figure elements.

So far my experience with matplotlib is that a lot of manual placement seem to be required when coming down to multiple plots and legends.

I have a large figure, consisting of several dense plots.

I’m trying to plot the legend outside of the plots, but I find it bothersome how difficult it is to place the legend outside the plot.

With gnuplot, it’s as simple as:

set key outside top right

This also works perfectly fine with ‘multiplot’ (gnuplot automatic multiple plot layout).

With matplotlib, I have to do the following:

legend(bbox_to_anchor=(1, 1 + ?), loc=2)

but how do I calculate the vertical location? Do I have to go at random to align the legend to the plot axis? It also breaks wonderfully with multiple plots, since plot(xyz) seems to consider only the axis and nothing more.

Can’t we just have:

legend(loc=2, outside=true)

please? The vast majority of plots I do are too dense to have a legend inside the plot.

Also, related to my previous message (savefig bbox_inches=‘tight’ does not consider suptitle), the legend is not considered when constructing a ‘tight’ boundary box.

It seems to me that the legend is another obvious element that should be taken into account without resorting to bbox_extra_artists.

Thanks again.

Matplotlib is designed to give you maximum control over the figure elements
while still maintaining sensible defaults. This is helpful in some cases,
and not so helpful in others. In your case of placing a legend outside an
axes, calculating the location can be a bit of a trial-and-error, but if the
number of elements in your legend is going to be fixed, once you find a good
value, you can just leave it that way in your script.

The problem with trial-and-error is that I can clearly see a difference of the sub-pixel rendering of the legend's frame when a lot of subplots are present - hinting at a slight-but-nonzero difference between the axis and the legend.

This bothers me at a sub-conscious level.. :slight_smile:

Automatic determination of legend size is difficult because the size of the
text objects are not known until draw time. However, it is possible to
query your legend object for its bbox and then reposition your legend object
accordingly (but only after the initial draw). This is tricky, but do-able.

I wonder how gnuplot implements its automatic layout, since manual placement is never necessary, even in multiplot outputs (for those who are not familiar with gnuplot: multiplot implements simple grid layouts with fully automatic positioning of the subplots).

By the way, another possible command you might want is called "figlegend",
which is a legend that is attached to the figure object rather than the axes
object. It is plotted outside the axes, and has to be given all the objects
to list, but it might be what you want. See this example:

Thanks, I will look into it.

···

On Mon, 7 Mar 2011 09:08:29 -0600 Benjamin Root <ben.root-GrrYUJ3DTa8@...1455...> wrote:

Maybe you want to try something like

leg = legend([l1], ["Test"], borderaxespad=0,
             bbox_to_anchor=(1.02, 1), loc=2)

?

-JJ

···

On Mon, Mar 7, 2011 at 8:22 PM, Yuri D'Elia <wavexx@...867...> wrote:

With matplotlib, I have to do the following:

legend(bbox_to_anchor=(1, 1 + ?), loc=2)

but how do I calculate the vertical location?