How to align rows in matplotlib legend with 2 columns
Question:
I have an issue where some mathtext formatting is making some labels take up more vertical space than others, which causes them to not line up when placed in two columns in the legend. This is particularly important because the rows are also used to indicate related data.
Here is an example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.mathtext as mathtext
mpl.rc("font", family="Times New Roman",weight='normal')
plt.rcParams.update({'mathtext.default': 'regular' })
plt.plot(1,1, label='A')
plt.plot(2,2, label='B')
plt.plot(3,3, label='C')
plt.plot(4,4,label='$A_{x}^{y}$')
plt.plot(5,5,label='$B_{x}^{y}$')
plt.plot(6,6,label='$C_{x}^{y}$')
plt.legend(fontsize='xx-large', ncol=2)
plt.show()
This generates a figure like so:
For a while, I was able to “fake it” a bit by adding some empty subscripts and superscripts, however this only works when the plot is exported to pdf. It does not appear to work when exporting to png. How can I spread out the first column of labels so that they line up with the second column?
Answers:
You could set the handleheight
keyword argument to a number which is just large enough that the height of the handle is larger than the space taken by the font. This makes the text appear aligned. Doing so may require to set the labelspacing
to a small number, in order not to make the legend appear too big.
plt.legend(fontsize='xx-large', ncol=2,handleheight=2.4, labelspacing=0.05)
The drawback of this method, as can be seen in the picture, is that the lines shift upwards compared to the text’s baseline. It would probably depent on the usage case if this is acceptable or not.
In case it is not, one needs to dig a little deeper. The following subclasses HandlerLine2D
(which is the Handler for Lines) in order to set a slightly different position to the lines. Depending on the total legend size, font size etc. one would need to adapt the number xx
in the SymHandler
class.
from matplotlib.legend_handler import HandlerLine2D
import matplotlib.lines
class SymHandler(HandlerLine2D):
def create_artists(self, legend, orig_handle,xdescent, ydescent, width, height, fontsize, trans):
xx= 0.6*height
return super(SymHandler, self).create_artists(legend, orig_handle,xdescent, xx, width, height, fontsize, trans)
leg = plt.legend(handler_map={matplotlib.lines.Line2D: SymHandler()},
fontsize='xx-large', ncol=2,handleheight=2.4, labelspacing=0.05)
I have an issue where some mathtext formatting is making some labels take up more vertical space than others, which causes them to not line up when placed in two columns in the legend. This is particularly important because the rows are also used to indicate related data.
Here is an example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.mathtext as mathtext
mpl.rc("font", family="Times New Roman",weight='normal')
plt.rcParams.update({'mathtext.default': 'regular' })
plt.plot(1,1, label='A')
plt.plot(2,2, label='B')
plt.plot(3,3, label='C')
plt.plot(4,4,label='$A_{x}^{y}$')
plt.plot(5,5,label='$B_{x}^{y}$')
plt.plot(6,6,label='$C_{x}^{y}$')
plt.legend(fontsize='xx-large', ncol=2)
plt.show()
This generates a figure like so:
For a while, I was able to “fake it” a bit by adding some empty subscripts and superscripts, however this only works when the plot is exported to pdf. It does not appear to work when exporting to png. How can I spread out the first column of labels so that they line up with the second column?
You could set the handleheight
keyword argument to a number which is just large enough that the height of the handle is larger than the space taken by the font. This makes the text appear aligned. Doing so may require to set the labelspacing
to a small number, in order not to make the legend appear too big.
plt.legend(fontsize='xx-large', ncol=2,handleheight=2.4, labelspacing=0.05)
The drawback of this method, as can be seen in the picture, is that the lines shift upwards compared to the text’s baseline. It would probably depent on the usage case if this is acceptable or not.
In case it is not, one needs to dig a little deeper. The following subclasses HandlerLine2D
(which is the Handler for Lines) in order to set a slightly different position to the lines. Depending on the total legend size, font size etc. one would need to adapt the number xx
in the SymHandler
class.
from matplotlib.legend_handler import HandlerLine2D
import matplotlib.lines
class SymHandler(HandlerLine2D):
def create_artists(self, legend, orig_handle,xdescent, ydescent, width, height, fontsize, trans):
xx= 0.6*height
return super(SymHandler, self).create_artists(legend, orig_handle,xdescent, xx, width, height, fontsize, trans)
leg = plt.legend(handler_map={matplotlib.lines.Line2D: SymHandler()},
fontsize='xx-large', ncol=2,handleheight=2.4, labelspacing=0.05)