Matplotlib: Adjust legend location/position

legend, matplotlib, plot, position, python

Solution

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:

Problem

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() ``` 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.

Original source