Search code examples
pythonmatplotliblegendlegend-properties

How to make matplotlib show legend which falls out of figure?


I draw a graph but unfortunately my legend falls out of the figure. How can I correct it? I put a dummy code to illustrate it: enter image description here

from matplotlib import pyplot as plt
from bisect import bisect_left,bisect_right
import numpy as np
global ranOnce
ranOnce=False
def threeLines(x):
    """Draws a function which is a combination of two lines intersecting in
    a point one with a large slope and one with a small slope.
    """
    start=0
    mid=5
    end=20
    global ranOnce,slopes,intervals,intercepts;
    if(not ranOnce):
        slopes=np.array([5,0.2,1]);
        intervals=[start,mid,end]
        intercepts=[start,(mid-start)*slopes[0]+start,(end-mid)*slopes[1]+(mid-start)*slopes[0]+start]
        ranOnce=True;
    place=bisect_left(intervals,x)
    if place==0:
        y=(x-intervals[place])*slopes[place]+intercepts[place];
    else:    
        y=(x-intervals[place-1])*slopes[place-1]+intercepts[place-1];
    return y;
def threeLinesDrawer(minimum,maximum):
    t=np.arange(minimum,maximum,1)
    fig=plt.subplot(111)
    markerSize=400;
    fig.scatter([minimum,maximum],[threeLines(minimum),threeLines(maximum)],marker='+',s=markerSize)

    y=np.zeros(len(t));
    for i in range(len(t)):
        y[i]=int(threeLines(t[i]))
    fig.scatter(t,y)
    fig.grid(True)
    fig.set_xlabel('Y')
    fig.set_ylabel('X')
    legend1 = plt.Circle((0, 0), 1, fc="r")
    legend2 = plt.Circle((0, 0), 1, fc="b")
    legend3 = plt.Circle((0, 0), 1, fc="g")
    fig.legend([legend1,legend2,legend3], ["p(y|x) likelihood","Max{p(y|x)} for a specific x","Y distribution"],
    bbox_to_anchor=(0., 1.02, 1., .102), loc=3,ncol=2, mode="expand", borderaxespad=0.)

threeLinesDrawer(0,20)
plt.show()

Solution

  • You can adjust the space that a set of axes takes within a figure by modifying the subplot parameters. For example, add the following line just before plt.show():

    plt.subplots_adjust(top=.9, bottom=.1, hspace=.1, left=.1, right=.9, wspace=.1)
    

    You should tweak the above values as you see fit in the range of [0, 1]. Feel free to get rid of the parameters you're not interested in tweaking (e.g. since you only have one axis in your figure, you won't care about the hspace and wspace parameters, which modify the spacing between subplots). These settings can also be modified through the plt.show() GUI, but you'd have to do it every time you run the script. A set of good settings for your case is the following:

    plt.subplots_adjust(top=.83, bottom=.08, left=.08, right=.98)
    

    For doing this adjustment automatically, you can try using tight_layout(). Add the following line just before plt.show():

    plt.tight_layout()
    

    This won't necessarily give the intended results in every case, though.