Get legend as a separate picture in Matplotlib

后端 未结 9 895
情歌与酒
情歌与酒 2020-11-29 03:50

I\'m developing a Web application and want to display a figure and its legend in different locations on the page. Which means I need to save the legend as a separate png fil

相关标签:
9条回答
  • 2020-11-29 04:17

    You may limit the saved region of a figure to the bounding box of the legend using the bbox_inches argument to fig.savefig. Below to versions of a function which you can simply call with the legend you want to save as argument. You may either use the legend created in the original figure here (and remove it afterwards, legend.remove()) or you may create a new figure for the legend and simply use the function as it is.

    Export legend boundingbox

    In case the complete legend shall be saved, the bounding box supplied to the bbox_inches argument would be simply the transformed bounding box of the legend. This works well if the legend has no border around it.

    import matplotlib.pyplot as plt
    
    colors = ["crimson", "purple", "gold"]
    f = lambda m,c: plt.plot([],[],marker=m, color=c, ls="none")[0]
    handles = [f("s", colors[i]) for i in range(3)]
    labels = colors
    legend = plt.legend(handles, labels, loc=3, framealpha=1, frameon=False)
    
    def export_legend(legend, filename="legend.png"):
        fig  = legend.figure
        fig.canvas.draw()
        bbox  = legend.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
        fig.savefig(filename, dpi="figure", bbox_inches=bbox)
    
    export_legend(legend)
    plt.show()
    

    Export extended legend bounding box

    If there is a border around the legend, the above solution may be suboptimal. In this case it makes sense to extend the bounding box by some pixels to include the border to its full.

    import numpy as np
    import matplotlib.pyplot as plt
    
    colors = ["crimson", "purple", "gold"]
    f = lambda m,c: plt.plot([],[],marker=m, color=c, ls="none")[0]
    handles = [f("s", colors[i]) for i in range(3)]
    labels = colors
    legend = plt.legend(handles, labels, loc=3, framealpha=1, frameon=True)
    
    def export_legend(legend, filename="legend.png", expand=[-5,-5,5,5]):
        fig  = legend.figure
        fig.canvas.draw()
        bbox  = legend.get_window_extent()
        bbox = bbox.from_extents(*(bbox.extents + np.array(expand)))
        bbox = bbox.transformed(fig.dpi_scale_trans.inverted())
        fig.savefig(filename, dpi="figure", bbox_inches=bbox)
    
    export_legend(legend)
    plt.show()
    

    0 讨论(0)
  • 2020-11-29 04:19

    Inspired by Maxim and ImportanceOfBeingErnest's answers,

    def export_legend(ax, filename="legend.pdf"):
        fig2 = plt.figure()
        ax2 = fig2.add_subplot()
        ax2.axis('off')
        legend = ax2.legend(*ax.get_legend_handles_labels(), frameon=False, loc='lower center', ncol=10,)
        fig  = legend.figure
        fig.canvas.draw()
        bbox  = legend.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
        fig.savefig(filename, dpi="figure", bbox_inches=bbox)
    

    which allows me to save legend horizontally in a separate file. As an example

    0 讨论(0)
  • 2020-11-29 04:19

    I'd like to add a small contribution for the specific case where your legend is customized such as here: https://matplotlib.org/3.1.1/gallery/text_labels_and_annotations/custom_legends.html

    In that case, you might have to go for a different method. I've been exposed to that problem and the answers above did not work for me.

    The code below sets-up the legend.

        import cmocean
        import matplotlib
        from matplotlib.lines import Line2D
    
        lightcmap = cmocean.tools.lighten(cmo.solar, 0.7)
        custom_legend = []
        custom_legend_strings=['no impact - high confidence', 'no impact - low confidence', 'impact - low confidence', 'impact - high confidence']
    
        for nbre_classes in range(len(custom_legend_strings)):
            custom_legend.append(Line2D([0], [0], color=lightcmap(nbre_classes/len(custom_legend_strings)), lw=4))
               
    

    I think because this kind of legend is attached the axes, a little trick was necessary :

    center the legend with a big font to make it take most of the available space and do not erase but set the axes to off :

        fig,ax = plt.subplots(figsize=(10,10))
        ax.legend(custom_legend,custom_legend_strings, loc = 10, fontsize=30)
        plt.axis('off')
        fig.savefig('legend.png', bbox_inches='tight')
    

    The result is :

    the result

    0 讨论(0)
  • 2020-11-29 04:19

    In November 2020, I tried almost everything on this post, but none worked for me. After struggling for a while, I found a solution that does what I want.

    Pretend you want to draw a figure and a legend separately that looks like below (apparently I don't have enough reputation to embed pictures in a post; click the links to see the picture).

    import matplotlib.pyplot as plt
    %matplotlib inline
    
    fig, ax = plt.subplots()
    
    ax.plot([1, 2, 3], label="test1")
    ax.plot([3, 2, 1], label="test2")
    
    ax.legend()
    

    target figure

    You can separate the figure and the legend in two different ax objects:

    fig, [ax1, ax2] = plt.subplots(1, 2)
    
    ax1.plot([1, 2, 3], label="test1")
    ax1.plot([3, 2, 1], label="test2")
    
    ax2.plot([1, 2, 3], label="test1")
    ax2.plot([3, 2, 1], label="test2")
    h, l = ax2.get_legend_handles_labels()
    ax2.clear()
    ax2.legend(h, l, loc='upper left')
    ax2.axis('off')
    

    fixed figure 1

    You can easily control where the legend should go:

    fig, [ax1, ax2] = plt.subplots(2, 1)
    
    ax1.plot([1, 2, 3], label="test1")
    ax1.plot([3, 2, 1], label="test2")
    
    ax2.plot([1, 2, 3], label="test1")
    ax2.plot([3, 2, 1], label="test2")
    h, l = ax2.get_legend_handles_labels()
    ax2.clear()
    ax2.legend(h, l, loc='upper left')
    ax2.axis('off')
    

    fixed figure 2

    0 讨论(0)
  • 2020-11-29 04:24

    I've found that the easiest way is just to create your legend and then just turn off the axis with plt.gca().set_axis_off():

    # Create a color palette
    palette = dict(zip(['one', 'two'], ['b', 'g']))
    # Create legend handles manually
    handles = [mpl.patches.Patch(color=palette[x], label=x) for x in palette.keys()]
    # Create legend
    plt.legend(handles=handles)
    # Get current axes object and turn off axis
    plt.gca().set_axis_off()
    plt.show()
    

    0 讨论(0)
  • 2020-11-29 04:29

    This could work:

    import pylab
    fig = pylab.figure()
    figlegend = pylab.figure(figsize=(3,2))
    ax = fig.add_subplot(111)
    lines = ax.plot(range(10), pylab.randn(10), range(10), pylab.randn(10))
    figlegend.legend(lines, ('one', 'two'), 'center')
    fig.show()
    figlegend.show()
    figlegend.savefig('legend.png')
    

    alt text

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