Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib: Adjust legend location/position

I'm creating a figure with multiple subplots. One of these subplots is giving me some trouble, as none of the axes corners or centers are free (or can be freed up) for placing the legend. What I'd like to do is to have the legend placed somewhere in between the 'upper left' and 'center left' locations, while keeping the padding between it and the y-axis equal to the legends in the other subplots (that are placed using one of the predefined legend location keywords).

I know I can specify a custom position by using loc=(x,y), but then I can't figure out how to get the padding between the legend and the y-axis to be equal to that used by the other legends. Would it be possible to somehow use the borderaxespad property of the first legend? Though I'm not succeeding at getting that to work.

Any suggestions would be most welcome!

Edit: Here is a (very simplified) illustration of the problem:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 2, sharex=False, sharey=False)
ax[0].axhline(y=1, label='one')
ax[0].axhline(y=2, label='two')
ax[0].set_ylim([0.8,3.2])
ax[0].legend(loc=2)

ax[1].axhline(y=1, label='one')
ax[1].axhline(y=2, label='two')
ax[1].axhline(y=3, label='three')
ax[1].set_ylim([0.8,3.2])
ax[1].legend(loc=2)

plt.show()

enter image description here

What I'd like is that the legend in the right plot is moved down somewhat so it no longer overlaps with the line. As a last resort I could change the axis limits, but I would very much like to avoid that.

like image 642
Caleris Avatar asked Jun 05 '14 13:06

Caleris


People also ask

How do I change the position of a legend in Matplotlib?

To change the position of a legend in Matplotlib, you can use the plt. legend() function. The default location is “best” – which is where Matplotlib automatically finds a location for the legend based on where it avoids covering any data points.

How do I align a legend in Matplotlib?

MatPlotLib with Python Plot line1 and line2 using plot() method. Place a legend on the figure. Use bbox_to_anchor to set the position and make horizontal alignment of the legend elements. To display the figure, use show() method.

How do I move the legend outside in Matplotlib?

In Matplotlib, to set a legend outside of a plot you have to use the legend() method and pass the bbox_to_anchor attribute to it. We use the bbox_to_anchor=(x,y) attribute. Here x and y specify the coordinates of the legend.


2 Answers

I saw the answer you posted and tried it out. The problem however is that it is also depended on the figure size.

Here's a new try:

import numpy
import matplotlib.pyplot as plt


x = numpy.linspace(0, 10, 10000)
y = numpy.cos(x) + 2.

x_value = .014    #Offset by eye
y_value = .55

fig, ax = plt.subplots(1, 2, sharex = False, sharey = False)
fig.set_size_inches(50,30)

ax[0].plot(x, y, label = "cos")
ax[0].set_ylim([0.8,3.2])
ax[0].legend(loc=2)

line1 ,= ax[1].plot(x,y)
ax[1].set_ylim([0.8,3.2])

axbox = ax[1].get_position()

fig.legend([line1], ["cos"], loc = (axbox.x0 + x_value, axbox.y0 + y_value))

plt.show()

So what I am now doing is basically getting the coordinates from the subplot. I then create the legend based on the dimensions of the entire figure. Hence, the figure size does not change anything to the legend positioning anymore.

With the values for x_value and y_value the legend can be positioned in the subplot. x_value has been eyeballed for a good correspondence with the "normal" legend. This value can be changed at your desire. y_value determines the height of the legend.

enter image description here

Good luck!

like image 190
The Dude Avatar answered Oct 20 '22 01:10

The Dude


After spending way too much time on this, I've come up with the following satisfactory solution (the Transformations Tutorial definitely helped):

bapad = plt.rcParams['legend.borderaxespad']
fontsize = plt.rcParams['font.size']
axline = plt.rcParams['axes.linewidth']  #need this, otherwise the result will be off by a few pixels
pad_points = bapad*fontsize + axline  #padding is defined in relative to font size
pad_inches = pad_points/72.0  #convert from points to inches
pad_pixels = pad_inches*fig.dpi  #convert from inches to pixels using the figure's dpi

Then, I found that both of the following work and give the same value for the padding:

# Define inverse transform, transforms display coordinates (pixels) to axes coordinates
inv = ax[1].transAxes.inverted()
# Inverse transform two points on the display and find the relative distance
pad_axes = inv.transform((pad_pixels, 0)) - inv.transform((0,0))  
pad_xaxis = pad_axes[0]

or

# Find how may pixels there are on the x-axis
x_pixels = ax[1].transAxes.transform((1,0)) - ax[1].transAxes.transform((0,0))
# Compute the ratio between the pixel offset and the total amount of pixels 
pad_xaxis = pad_pixels/x_pixels[0]

And then set the legend with:

ax[1].legend(loc=(pad_xaxis,0.6))

Plot:

like image 32
Caleris Avatar answered Oct 20 '22 01:10

Caleris