Search code examples
pythonmatplotliblabellegendmarkers

How to set a title above each marker which represents a same label


I have a first version of legend in the following plot :

2 columns into legend

with the following code :

# Plot and save : kmax = 0.3
p11, = plt.plot([0], marker='None',
               linestyle='None', label='$k_{max} = 0.3$')
p1, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,1], '-b', label = '$GC_{sp}$')
p2, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,2], '-r', label = '$GC_{ph}$')
p3, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,3], '-y', label = '$WL$')
p4, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,4], '-g', label = '$GC_{ph} + WL + XC$')
p5, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,5], '-m', label = \

                                                               '$GC_{sp} + (GC_{ph} + WL + XC)$')
# Plot and save : kmax = 1.0
p12, = plt.plot([0], marker='None',
               linestyle='None', label='$k_{max} = 1.0$')
p6, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,1], '--b', label = '$GC_{sp}$')
p7, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,2], '--r', label = '$GC_{ph}$')
p8, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,3], '--y', label = '$WL$')
p9, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,4], '--g', label = '$GC_{ph} + WL + XC$')
p10, =plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,5], '--m', label = \

                                                               '$GC_{sp} + (GC_{ph} + WL + XC)$')
plt.legend(fontsize=14, loc='best', ncol=2, handleheight=1.4, labelspacing=0.05)

As you can see, I put a title (k_max = 0.3 and k_max = 1.0) for each column of markers and columns.

Now, to avoid this redundancy, I am trying to merge all duplicated labels while keeping the title for each marker by doing :

from matplotlib.legend_handler import HandlerTuple

# Plot and save : kmax = 0.3
p11, = plt.plot([0], marker='None', linestyle='None')
p1, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,1], '-b')
p2, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,2], '-r')
p3, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,3], '-y')
p4, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,4], '-g')
p5, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,5], '-m')

# Plot and save : kmax = 1.0
p12, = plt.plot([0], marker='None', linestyle='None')
p6, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,1], '--b') 
p7, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,2], '--r') 
p8, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,3], '--y') 
p9, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,4], '--g') 
p10, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,5], '--m') 

l = plt.legend([(p1,p6), (p2,p7), (p3,p8), (p4,p9), (p5,p10)], ['$GC_{sp}$', \
               '$GC_{ph}$', '$WL$', '$GC_{ph} + WL + XC$', '$GC_{sp} + (GC_{ph} + WL + XC)$'], \
               fontsize=14, loc='best', handlelength=2.5, handleheight=1.4, labelspacing=0.05, \
               handler_map={tuple: HandlerTuple(ndivide=None)})

This way, I get the following figure :

same labels for 2 markers

Then, 2 issues occurs :

1) The space between 2 markers is too small compared to the first figure above : how to insert a bigger space between markers and more length for the markers themselves (for example, having 4 dash-lines for the dash-line marker, like for the 4 dash-lines marker on the legend of the first figure above at the beginning of my post)

2) How to put the titles k_max = 0.3 and k_max = 1.0 above each column of markers ? : this way, I could identify quickly the case I consider on the plot (like I did on the first figure above but there was redundancy by repeating twice the displaying of all labels).


Solution

  • To tackle your issues you can try the following:

    1.1 To increase the space between the markers you can provide the additional parameter pad to HandlerTuple() (from here). It will adjust the spacing between the different marker sections. This will look like:

    l = plt.legend(..., handler_map={tuple: HandlerTuple(ndivide=None, pad=2)})
    

    1.2 To add more width to the markers you could increase the value for the handlelength parameter like this:

    l = plt.legend(..., handlelength=6.5, ...)
    

    The resulting with these values will look like this: Part 1 solution

    1. To specify a description above the marker columns you can modify your p11, = plt.plot([0], marker='None', linestyle='None') and p12, = plt.plot([0], marker='None', linestyle='None') lines to the following:
    # your code:
    # p11, = plt.plot([0], marker='None', linestyle='None')
    # p12, = plt.plot([0], marker='None', linestyle='None')
    p11, = plt.plot([], marker='$0.3$', linestyle='None', color='k', markersize=18, mec='None')
    p12, = plt.plot([], marker='$1.0$', linestyle='None', color='k', markersize=18, mec='None')
    

    Replacing [0] with an empty list [] will result in really nothing is plotted. mec='None' will remove the markers edge color. Without it, the markers look like written in bold.

    To show these "lines" in the legend add the following to legend:

    l = plt.legend([(p11,p12), ....], ['$k_{max}$', ...], ...)
    

    With these adjustments the legend should look like:

    Part 2 solution

    The complete code should then look like this:

    # Plot and save : kmax = 0.3
    p11, = plt.plot([], marker=r'$0.3$', linestyle='None', color='k', markersize=18, mec='None')
    p1, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,1], '-b')
    p2, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,2], '-r')
    p3, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,3], '-y')
    p4, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,4], '-g')
    p5, = plt.plot(FoM_vs_Density_array_1[:,0],FoM_vs_Density_array_1[:,5], '-m')
    
    # Plot and save : kmax = 1.0
    p12, = plt.plot([], marker='$1.0$', linestyle='None', color='k', markersize=18, mec='None')
    p6, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,1], '--b')
    p7, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,2], '--r')
    p8, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,3], '--y')
    p9, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,4], '--g')
    p10, = plt.plot(FoM_vs_Density_array_2[:,0],FoM_vs_Density_array_2[:,5], '--m')
    
    l = plt.legend([(p11,p12),(p1,p6), (p2,p7), (p3,p8), (p4,p9), (p5,p10)], ['$k_{max}$','$GC_{sp}$', \
                   '$GC_{ph}$', '$WL$', '$GC_{ph} + WL + XC$', '$GC_{sp} + (GC_{ph} + WL + XC)$'], \
                   fontsize=14, loc='best', handlelength=6.5, handleheight=1.4, labelspacing=0.05, \
                   handler_map={tuple: HandlerTuple(ndivide=None, pad=2)})
    

    To format the column names like you show in your first picture use:

    # change marker and markersize
    p11, = plt.plot([], marker=r'$k_{max}=0.3$', linestyle='None', color='k', markersize=45, mec='None')
    p12, = plt.plot([], marker='$k_{max}=1.0$', linestyle='None', color='k', markersize=45, mec='None')
    
    # change plt.legend, use 'borderpad' parameter to increase the legend box,
    # otherwise part of the column name will be written out of bounds
    #                                                                         | change here
    l = plt.legend([(p11,p12),(p1,p6), (p2,p7), (p3,p8), (p4,p9), (p5,p10)], ['','$GC_{sp}$', \
                   '$GC_{ph}$', '$WL$', '$GC_{ph} + WL + XC$', '$GC_{sp} + (GC_{ph} + WL + XC)$'], \
                   fontsize=14, loc='best', handlelength=6.5, handleheight=1.4, labelspacing=0.05, \
                   borderpad=0.6, # change here
                   handler_map={tuple: HandlerTuple(ndivide=None, pad=2)})
    

    This will result in:

    Part 2 alternative solution