关于python:如何将图例放出情节

How to put the legend out of the plot

我有一系列20个图(不是子图),用一个数字绘制。我想让这个传说在盒子外面。同时,我不想更改轴,因为图形的大小会减小。如有以下问题,请帮助我:

  • 我想将图例框保留在绘图区域之外。(我希望图例位于绘图区右侧的外部)。
  • 是否仍要减小图例框中文本的字体大小,使图例框的大小变小。

  • 有很多方法可以满足你的需要。要添加@inalis和@navi已经说过的内容,可以使用bbox_to_anchor关键字参数将图例部分放在轴之外和/或减小字体大小。

    在考虑减小字体大小(这会使内容难以阅读)之前,请尝试在不同的位置放置图例:

    那么,让我们从一个通用示例开始:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import matplotlib.pyplot as plt
    import numpy as np

    x = np.arange(10)

    fig = plt.figure()
    ax = plt.subplot(111)

    for i in xrange(5):
        ax.plot(x, i * x, label='$y = %ix$' % i)

    ax.legend()

    plt.show()

    alt text

    如果我们做同样的事情,但是使用bbox_to_anchor关键字参数,我们可以将图例稍微移到轴边界之外:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import matplotlib.pyplot as plt
    import numpy as np

    x = np.arange(10)

    fig = plt.figure()
    ax = plt.subplot(111)

    for i in xrange(5):
        ax.plot(x, i * x, label='$y = %ix$' % i)

    ax.legend(bbox_to_anchor=(1.1, 1.05))

    plt.show()

    alt text

    类似地,您可以使图例更水平和/或放在图形的顶部(我还打开圆角和简单的阴影):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import matplotlib.pyplot as plt
    import numpy as np

    x = np.arange(10)

    fig = plt.figure()
    ax = plt.subplot(111)

    for i in xrange(5):
        line, = ax.plot(x, i * x, label='$y = %ix$'%i)

    ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05),
              ncol=3, fancybox=True, shadow=True)
    plt.show()

    alt text

    或者,您可以缩小当前绘图的宽度,并将图例完全置于图形的轴之外:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import matplotlib.pyplot as plt
    import numpy as np

    x = np.arange(10)

    fig = plt.figure()
    ax = plt.subplot(111)

    for i in xrange(5):
        ax.plot(x, i * x, label='$y = %ix$'%i)

    # Shrink current axis by 20%
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

    # Put a legend to the right of the current axis
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

    plt.show()

    alt text

    以类似的方式,您可以垂直缩小绘图,并在底部放置水平图例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import matplotlib.pyplot as plt
    import numpy as np

    x = np.arange(10)

    fig = plt.figure()
    ax = plt.subplot(111)

    for i in xrange(5):
        line, = ax.plot(x, i * x, label='$y = %ix$'%i)

    # Shrink current axis's height by 10% on the bottom
    box = ax.get_position()
    ax.set_position([box.x0, box.y0 + box.height * 0.1,
                     box.width, box.height * 0.9])

    # Put a legend below current axis
    ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),
              fancybox=True, shadow=True, ncol=5)

    plt.show()

    alt text

    看看Matplotlib传奇指南。你也可以看看plt.figlegend()。无论如何,希望这能帮上一点忙!


    (bbox_to_anchor)将传奇

    传奇是一bounding放置盒的内侧轴对plt.legendloc参数使用。例如,loc="upper right"地方传说中的bounding框右上方角落,它扩展到从(0,0)默认(1,1)在轴坐标(或标注(x0,y0, width, height)=(0,0,1,1)bounding盒)。

    在对轴的地方传说的bounding盒,一个元组可以指定坐标轴的(x0,y0)(左下角的传奇。

    1
    plt.legend(loc=(1.04,0))

    然而,更多的通用方法是手动指定的bounding箱应放置到它的传说,bbox_to_anchor参数使用。一个CAN restrict自己到供应只有部分的(x0,y0)BBOX。这个扩展的零跨度10箱,其中传奇将扩大在给定的方向loc参数。例如

    1
    plt.legend(bbox_to_anchor=(1.04,1), loc="upper left")

    传说的地方超出了轴,这样,左上角的位置是在(1.04,1)传奇中的坐标轴。

    在进一步的给出了一些例子,以及如何在不同的参数之间的modeInterplay样和ncols是如图所示。

    enter image description here

    1
    2
    3
    4
    5
    6
    7
    8
    l1 = plt.legend(bbox_to_anchor=(1.04,1), borderaxespad=0)
    l2 = plt.legend(bbox_to_anchor=(1.04,0), loc="lower left", borderaxespad=0)
    l3 = plt.legend(bbox_to_anchor=(1.04,0.5), loc="center left", borderaxespad=0)
    l4 = plt.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
                    mode="expand", borderaxespad=0, ncol=3)
    l5 = plt.legend(bbox_to_anchor=(1,0), loc="lower right",
                    bbox_transform=fig.transFigure, ncol=3)
    l6 = plt.legend(bbox_to_anchor=(0.4,0.8), loc="upper right")

    详细信息关于如何解释的问题bbox_to_anchor到4元,在l4,可以发现在这一问题。在mode="expand"传奇水平扩大了内幕bounding盒由四元组。垂直扩展的传说是,国有企业这一问题。

    有时,它可能是有用的到指定的信箱中bounding坐标轴坐标图来代替。这是显示在上面的例子中l5参数是用在bbox_transform,把传说中的左下角出现。

    postprocessing

    有传说,往往超出了观察到的情况undesired导联轴,这是完全的或局部的出现在画布上。

    解决方案:这是问题

    • 在subplot参数调整一个可以调整的参数,如subplot空间轴,以不出现(与内侧,从而把更多的空间利用到plt.subplots_adjust传奇)。例如

      1
      plt.subplots_adjust(right=0.7)

      叶片的空间在30%右手侧的数字,可以在一个地方的传说。

    • 密封布置自动调整到plt.tight_layout允许使用的参数,如subplot元素在图对图的边坐着别动。不幸的是,传说是不考虑在这个自动的,但我们可以供应整个区的矩形框,将subplots(包括标签)插入安装。

      1
      plt.tight_layout(rect=[0,0,0.75,1])
    • 《节能与bbox_inches ="tight"身材该参数可以用来对plt.savefigbbox_inches ="tight"拯救所有的艺术家出现在画布上,这样的安装(包括传奇)是为保存地区。如果需要,该图是自动调整大小。

      1
      plt.savefig("output.png", bbox_inches="tight")
    • 自动调整子批次参数在这个答案中可以找到一种自动调整子批次位置的方法,使图例适合画布而不更改图形大小:创建具有精确大小且无填充的图形(以及轴外的图例)。

    上述案例的比较:好的。

    enter image description here好的。选择

    人物传说人们可以在图形中使用图例,而不是轴,matplotlib.figure.Figure.legend。这对于Matplotlib版本>=2.1特别有用,因为它不需要特殊参数。好的。

    1
    fig.legend(loc=7)

    为人物不同轴上的所有艺术家创建一个图例。图例使用loc参数放置,类似于如何将其放置在轴内,但参照整个图形-因此,它将在轴外自动放置。剩下的就是调整子批次,使图例和轴之间没有重叠。在这里,"调整子批次参数"将有帮助。一个例子:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import numpy as np
    import matplotlib.pyplot as plt

    x = np.linspace(0,2*np.pi)
    colors=["#7aa0c4","#ca82e1" ,"#8bcd50","#e18882"]
    fig, axes = plt.subplots(ncols=2)
    for i in range(4):
        axes[i//2].plot(x,np.sin(x+i), color=colors[i],label="y=sin(x+{})".format(i))

    fig.legend(loc=7)
    fig.tight_layout()
    fig.subplots_adjust(right=0.75)  
    plt.show()

    enter image description here好的。

    专用子批次轴内的图例使用bbox_to_anchor的另一种选择是将图例放置在其专用子地块轴(lax)中。由于图例子块应该小于绘图,我们可以在轴创建时使用gridspec_kw={"width_ratios":[4,1]}。我们可以隐藏轴lax.axis("off"),但仍然在其中添加了一个传说。图例处理和标签需要通过h,l = ax.get_legend_handles_labels()从实际绘图中获得,然后可以提供给lax子批次lax.legend(h,l)中的图例。下面是一个完整的例子。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import matplotlib.pyplot as plt
    plt.rcParams["figure.figsize"] = 6,2

    fig, (ax,lax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[4,1]})
    ax.plot(x,y, label="y=sin(x)")
    ....

    h,l = ax.get_legend_handles_labels()
    lax.legend(h,l, borderaxespad=0)
    lax.axis("off")

    plt.tight_layout()
    plt.show()

    这将生成一个与上面的绘图在视觉上非常相似的绘图:好的。

    enter image description here好的。

    我们也可以使用第一个轴放置图例,但使用图例轴的bbox_transform,好的。

    1
    2
    ax.legend(bbox_to_anchor=(0,0,1,1), bbox_transform=lax.transAxes)
    lax.axis("off")

    在这种方法中,我们不需要从外部获取图例句柄,但需要指定bbox_to_anchor参数。好的。进一步阅读和注释:

    • 考虑Matplotlib Legend指南,其中包含一些您想用Legends处理的其他内容的示例。
    • 一些用于放置饼图图例的示例代码可以直接在这个问题的答案中找到:python-图例与饼图重叠
    • loc参数可以采用数字而不是字符串,这会缩短调用时间,但是它们之间的映射并不十分直观。以下是供参考的映射:

    enter image description here好的。好啊。


    plot()呼叫之后,您只需拨打legend()呼叫,如下所示:

    1
    2
    3
    4
    5
    6
    # matplotlib
    plt.plot(...)
    plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))

    # Pandas
    df.myCol.plot().legend(loc='center left', bbox_to_anchor=(1, 0.5))

    结果如下:

    enter image description here


    创建字体属性

    1
    2
    3
    4
    5
    from matplotlib.font_manager import FontProperties

    fontP = FontProperties()
    fontP.set_size('small')
    legend([plot1],"title", prop=fontP)


    简单回答:您可以使用bbox_to_anchorbbox_extra_artistsbbox_inches='tight'

    更长的答案:您可以使用bbox_to_anchor手动指定图例框的位置,正如一些其他人在答案中指出的那样。

    但是,通常的问题是图例框被裁剪,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import matplotlib.pyplot as plt

    # data
    all_x = [10,20,30]
    all_y = [[1,3], [1.5,2.9],[3,2]]

    # Plot
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    ax.plot(all_x, all_y)

    # Add legend, title and axis labels
    lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
    ax.set_title('Title')
    ax.set_xlabel('x label')
    ax.set_ylabel('y label')

    fig.savefig('image_output.png', dpi=300, format='png')

    enter image description here

    为了防止图例框被裁剪,保存图形时,可以使用参数bbox_extra_artistsbbox_inches要求savefig在保存的图像中包含裁剪的元素:

    fig.savefig('image_output.png', bbox_extra_artists=(lgd,), bbox_inches='tight')

    示例(我只更改了最后一行以向fig.savefig()添加2个参数):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import matplotlib.pyplot as plt

    # data
    all_x = [10,20,30]
    all_y = [[1,3], [1.5,2.9],[3,2]]

    # Plot
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    ax.plot(all_x, all_y)

    # Add legend, title and axis labels
    lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
    ax.set_title('Title')
    ax.set_xlabel('x label')
    ax.set_ylabel('y label')    

    fig.savefig('image_output.png', dpi=300, format='png', bbox_extra_artists=(lgd,), bbox_inches='tight')

    enter image description here

    我希望Matplotlib能够像Matlab那样在本地允许图例框的外部位置:

    1
    2
    3
    4
    5
    6
    7
    figure
    x = 0:.2:12;
    plot(x,besselj(1,x),x,besselj(2,x),x,besselj(3,x));
    hleg = legend('First','Second','Third',...
                  'Location','NorthEastOutside')
    % Make the text of the legend italic and color it brown
    set(hleg,'FontAngle','italic','TextColor',[.3,.2,.1])

    enter image description here


    要将图例放置在绘图区域之外,请使用locbbox_to_anchor的关键字legend()。例如,以下代码将图例放置在绘图区域的右侧:

    1
    legend(loc="upper left", bbox_to_anchor=(1,1))

    有关详细信息,请参阅图例指南


    简短回答:在图例上调用Draggable,并将其交互移动到所需的任何位置:

    1
    ax.legend().draggable()

    长答案:如果您更喜欢以交互方式/手动方式而不是以编程方式放置图例,则可以切换图例的可拖动模式,以便将其拖动到所需的任何位置。请检查以下示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import matplotlib.pylab as plt
    import numpy as np
    #define the figure and get an axes instance
    fig = plt.figure()
    ax = fig.add_subplot(111)
    #plot the data
    x = np.arange(-5, 6)
    ax.plot(x, x*x, label='y = x^2')
    ax.plot(x, x*x*x, label='y = x^3')
    ax.legend().draggable()
    plt.show()

    除了这里所有优秀的答案,如果可能的话,新版的matplotlibpylab可以自动决定把传说放在哪里,而不干扰情节。

    1
    pylab.legend(loc='best')

    如果可能,这将自动将图例放在远离数据的位置!Compare the use of loc='best'

    但是,如果没有地方可以在不重叠数据的情况下放置图例,那么您将希望尝试其他答案之一;使用loc="best"将永远不会将图例放在绘图之外。


    不完全是你要求的,但我发现这是同一问题的另一种选择。把传说变成半超现实主义,就像这样:matplotlib plot with semi transparent legend and semitransparent text box

    这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    fig = pylab.figure()
    ax = fig.add_subplot(111)
    ax.plot(x,y,label=label,color=color)
    # Make the legend transparent:
    ax.legend(loc=2,fontsize=10,fancybox=True).get_frame().set_alpha(0.5)
    # Make a transparent text box
    ax.text(0.02,0.02,yourstring, verticalalignment='bottom',
                         horizontalalignment='left',
                         fontsize=10,
                         bbox={'facecolor':'white', 'alpha':0.6, 'pad':10},
                         transform=self.ax.transAxes)

    如前所述,您也可以将图例放置在绘图中,或者稍微偏离到边缘。下面是一个使用plotly python api的例子,它是用ipython笔记本制作的。我是团队成员。

    首先,您需要安装必要的软件包:

    1
    2
    3
    4
    import plotly
    import math
    import random
    import numpy as np

    然后,按顺序安装:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    un='IPython.Demo'
    k='1fw3zw2o13'
    py = plotly.plotly(username=un, key=k)


    def sin(x,n):
    sine = 0
    for i in range(n):
        sign = (-1)**i
        sine = sine + ((x**(2.0*i+1))/math.factorial(2*i+1))*sign
    return sine

    x = np.arange(-12,12,0.1)

    anno = {
    'text': '$\\sum_{k=0}^{\\infty} \\frac {(-1)^k x^{1+2k}}{(1 + 2k)!}$',
    'x': 0.3, 'y': 0.6,'xref':"paper", 'yref':"paper",'showarrow': False,
    'font':{'size':24}
    }

    l = {
    'annotations': [anno],
    'title': 'Taylor series of sine',
    'xaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
    'yaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
    'legend':{'font':{'size':16},'bordercolor':'white','bgcolor':'#fcfcfc'}
    }

    py.iplot([{'x':x, 'y':sin(x,1), 'line':{'color':'#e377c2'}, 'name':'$x\\\\$'},\
          {'x':x, 'y':sin(x,2), 'line':{'color':'#7f7f7f'},'name':'$ x-\\frac{x^3}{6}$'},\
          {'x':x, 'y':sin(x,3), 'line':{'color':'#bcbd22'},'name':'$ x-\\frac{x^3}{6}+\\frac{x^5}{120}$'},\
          {'x':x, 'y':sin(x,4), 'line':{'color':'#17becf'},'name':'$ x-\\frac{x^5}{120}$'}], layout=l)

    这将创建图形,并允许您将图例保留在绘图本身中。如果没有设置,图例的默认设置是将其放置在绘图中,如图所示。

    enter image description here

    对于另一种放置方式,可以将图形的边缘与图例的边框紧密对齐,并删除边框线以获得更紧密的配合。

    enter image description here

    您可以使用代码或GUI移动和重新设置图例和图形的样式。要移动图例,可以通过指定x和y值<=1来在图形中定位图例。例如:

    • {"x" : 0,"y" : 0}--左下
    • {"x" : 1,"y" : 0}—右下角
    • {"x" : 1,"y" : 1}—右上角
    • {"x" : 0,"y" : 1}--左上
    • {"x" :.5,"y" : 0}底心
    • {"x": .5,"y" : 1}—上中

    在这种情况下,我们选择右上角的legendstyle = {"x" : 1,"y" : 1},文档中也描述了:

    enter image description here


    沿着这条线的一些东西对我有用。从Joe获取的一点代码开始,此方法修改窗口宽度,使其自动适应图形右侧的图例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    import matplotlib.pyplot as plt
    import numpy as np

    plt.ion()

    x = np.arange(10)

    fig = plt.figure()
    ax = plt.subplot(111)

    for i in xrange(5):
        ax.plot(x, i * x, label='$y = %ix$'%i)

    # Put a legend to the right of the current axis
    leg = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

    plt.draw()

    # Get the ax dimensions.
    box = ax.get_position()
    xlocs = (box.x0,box.x1)
    ylocs = (box.y0,box.y1)

    # Get the figure size in inches and the dpi.
    w, h = fig.get_size_inches()
    dpi = fig.get_dpi()

    # Get the legend size, calculate new window width and change the figure size.
    legWidth = leg.get_window_extent().width
    winWidthNew = w*dpi+legWidth
    fig.set_size_inches(winWidthNew/dpi,h)

    # Adjust the window size to fit the figure.
    mgr = plt.get_current_fig_manager()
    mgr.window.wm_geometry("%ix%i"%(winWidthNew,mgr.window.winfo_height()))

    # Rescale the ax to keep its original size.
    factor = w*dpi/winWidthNew
    x0 = xlocs[0]*factor
    x1 = xlocs[1]*factor
    width = box.width*factor
    ax.set_position([x0,ylocs[0],x1-x0,ylocs[1]-ylocs[0]])

    plt.draw()


    你也可以试试figlegend。可以创建独立于任何轴对象的图例。但是,您可能需要创建一些"虚拟"路径,以确保正确传递对象的格式。


    当我有一个巨大的传奇时,我的解决方案是使用额外的空图像布局。在下面的示例中,我画了4行,在底部,我用偏移量为顶部的图例(bbox_to_anchor)绘制图像,它不会被剪切。

    1
    2
    3
    4
    5
    f = plt.figure()
    ax = f.add_subplot(414)
    lgd = ax.legend(loc='upper left', bbox_to_anchor=(0, 4), mode="expand", borderaxespad=0.3)
    ax.autoscale_view()
    plt.savefig(fig_name, format='svg', dpi=1200, bbox_extra_artists=(lgd,), bbox_inches='tight')

    这是另一个解决方案,类似于添加bbox_extra_artistsbbox_inches,在savefig调用范围内不需要额外的艺术家。我之所以想到这个,是因为我生成了我大部分的函数内部的绘图。

    当你想写出来的时候,你不需要把所有的添加都添加到边界框中,而是可以提前将它们添加到Figure的艺术家中。使用类似于上面Franck Deroncourt的答案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import matplotlib.pyplot as plt

    # data
    all_x = [10,20,30]
    all_y = [[1,3], [1.5,2.9],[3,2]]

    # plotting function
    def gen_plot(x, y):
        fig = plt.figure(1)
        ax = fig.add_subplot(111)
        ax.plot(all_x, all_y)
        lgd = ax.legend( ["Lag" + str(lag) for lag in all_x], loc="center right", bbox_to_anchor=(1.3, 0.5))
        fig.artists.append(lgd) # Here's the change
        ax.set_title("Title")
        ax.set_xlabel("x label")
        ax.set_ylabel("y label")
        return fig

    # plotting
    fig = gen_plot(all_x, all_y)

    # No need for `bbox_extra_artists`
    fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")

    这是生成的图。


    下面是Matplotlib教程中的一个示例。这是一个更简单的例子,但我增加了图例的透明度,并添加了plt.show(),这样您就可以将其粘贴到交互式shell中并得到一个结果:

    1
    2
    3
    4
    5
    6
    import matplotlib.pyplot as plt
    p1, = plt.plot([1, 2, 3])
    p2, = plt.plot([3, 2, 1])
    p3, = plt.plot([2, 3, 1])
    plt.legend([p2, p1, p3], ["line 1","line 2","line 3"]).get_frame().set_alpha(0.5)
    plt.show()

    不知道你是否已经解决了你的问题…可能是的,但是…我只是使用字符串"outside"作为位置,就像在matlab中一样。我从Matplotlib进口了Pylab。代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from matplotlib as plt
    from matplotlib.font_manager import FontProperties
    ...
    ...
    t = A[:,0]
    sensors = A[:,index_lst]

    for i in range(sensors.shape[1]):
        plt.plot(t,sensors[:,i])

    plt.xlabel('s')
    plt.ylabel('°C')
    lgd = plt.legend(b,loc='center left', bbox_to_anchor=(1, 0.5),fancybox = True, shadow = True)

    点击查看绘图