Single legend item with two lines

后端 未结 2 665
梦如初夏
梦如初夏 2020-12-09 14:30

I\'d like to generate a custom matplotlib legend which, for each entry has two lines for each label as shown in this example:

From some researc

相关标签:
2条回答
  • 2020-12-09 14:35

    In the matplotlib legend guide there is a chapter about custom legend handlers. You could adapt it to your needs, e.g. like this:

    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.legend_handler import HandlerBase
    
    
    class AnyObjectHandler(HandlerBase):
        def create_artists(self, legend, orig_handle,
                           x0, y0, width, height, fontsize, trans):
            l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height], 
                                                    linestyle='--', color='k')
            l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height], color='r')
            return [l1, l2]
    
    
    x = np.linspace(0, 3)
    fig, axL = plt.subplots(figsize=(4,3))
    axR = axL.twinx()
    
    axL.plot(x, np.sin(x), color='k', linestyle='--')
    axR.plot(x, 100*np.cos(x), color='r')
    
    axL.set_ylabel('sin(x)', color='k')
    axR.set_ylabel('100 cos(x)', color='r')
    axR.tick_params('y', colors='r')
    
    plt.legend([object], ['label'],
               handler_map={object: AnyObjectHandler()})
    
    plt.show()
    

    In order to have multiple such entries, one can supply some tuple of parameters that the Handler then uses to draw the legend.

    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.legend_handler import HandlerBase
    
    
    class AnyObjectHandler(HandlerBase):
        def create_artists(self, legend, orig_handle,
                           x0, y0, width, height, fontsize, trans):
            l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height],
                               linestyle=orig_handle[1], color='k')
            l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height], 
                               color=orig_handle[0])
            return [l1, l2]
    
    
    x = np.linspace(0, 3)
    fig, axL = plt.subplots(figsize=(4,3))
    axR = axL.twinx()
    
    axL.plot(x, np.sin(x), color='k', linestyle='--')
    axR.plot(x, 100*np.cos(x), color='r')
    
    axL.plot(x, .3*np.sin(x), color='k', linestyle=':')
    axR.plot(x, 20*np.cos(x), color='limegreen')
    
    axL.set_ylabel('sin(x)', color='k')
    axR.set_ylabel('100 cos(x)', color='r')
    axR.tick_params('y', colors='r')
    
    plt.legend([("r","--"), ("limegreen",":")], ['label', "label2"],
               handler_map={tuple: AnyObjectHandler()})
    
    plt.show()
    

    0 讨论(0)
  • 2020-12-09 14:49

    Here is an inadequate solution I have come to after playing around. I post it in case it helps others, but would prefer to do it properly. It uses two columns and the label of the first as a '/' or one could equally use "and".

    import matplotlib.pyplot as plt
    
    x = np.linspace(0, 3)
    fig, axL = plt.subplots()
    axR = axL.twinx()
    
    axL.plot(x, np.sin(x), color='k', label='/')
    axR.plot(x, 100*np.cos(x), color='r', label='label')
    
    axL.set_ylabel('sin(x)', color='k')
    axR.set_ylabel('100 cos(x)', color='r')
    axR.tick_params('y', colors='r')
    
    handlesL, labelsL = axL.get_legend_handles_labels()
    handlesR, labelsR = axR.get_legend_handles_labels()
    handles = handlesL + handlesR
    labels = labelsL + labelsR
    axR.legend(handles, labels, loc='lower center', ncol=2, fontsize=16,
               handletextpad=0.4, columnspacing=0.4)
    

    0 讨论(0)
提交回复
热议问题