如何把传说从情节中删除

时间:2011-01-15 16:10:03

标签: python matplotlib legend

我有一系列20个图(不是子图)可以在一个图中制作。我希望传说能够在盒子之外。同时,我不想改变轴,因为图形的大小减少了。请帮助我解决以下问题:

  1. 我想将情节框保留在情节区域之外。 (我希望传说位于情节区域的右侧)。
  2. 无论如何,我减少了图例框内文字的字体大小,因此图例框的大小会很小。

17 个答案:

答案 0 :(得分:1511)

有很多方法可以做你想要的。要添加@inalis和@Navi已经说过的内容,您可以使用bbox_to_anchor关键字参数将图例部分放置在轴外和/​​或减小字体大小。

在考虑减小字体大小(这可能会让事情难以阅读)之前,请尝试将图例放在不同的地方:

所以,让我们从一个通用的例子开始:

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关键字参数,我们可以稍微将图例移到轴边界之外:

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

同样,你可以使图例更加水平和/或将它放在图的顶部(我也打开圆角和一个简单的阴影):

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

或者,您可以缩小当前图的宽度,并将图例完全放在图的轴外:

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

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

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 legend guide。您也可以查看plt.figlegend()。无论如何,希望有所帮助!

答案 1 :(得分:526)

放置图例(bbox_to_anchor

使用plt.legendloc参数将图例定位在轴的边界框内。
例如。 loc="upper right"将图例放置在边界框的右上角,默认情况下,在轴坐标(或边界框符号(0,0)中)从(1,1)范围扩展到(x0,y0, width, height)=(0,0,1,1)

要将图例放置在轴边界框之外,可以指定图例左下角的轴坐标的元组(x0,y0)

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

但是,更通用的方法是使用 bbox_to_anchor 参数手动指定应放置图例的边界框。可以限制自己仅提供bbox的(x0,y0)部分。这将创建一个零跨度框,其中图例将按loc参数指定的方向展开。 E.g。

<强>

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

将图例放在轴外,使图例的左上角位于轴坐标中的(1.04,1)位置。

下面给出了更多示例,其中还显示了modencols等不同参数之间的相互作用。

enter image description here

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的详细信息,可以在this question中找到。 mode="expand"在4元组给出的边界框内水平扩展图例。有关垂直展开的图例,请参阅this question

有时在图坐标中指定边界框而不是轴坐标可能很有用。这显示在上面的示例l5中,其中bbox_transform参数用于将图例放在图的左下角。

后处理

将图例放置在轴之外通常会导致不完整或完全偏离图形画布的情况。

此问题的解决方案是:

  • 调整子图参数
    可以通过使用plt.subplots_adjust调整子图参数,使轴在图中占用更少的空间(从而为图例留出更多空间)。例如。

    plt.subplots_adjust(right=0.7)
    

    在图的右侧留下30%的空间,可以放置图例。

  • 布局紧张
    使用plt.tight_layout允许自动调整子图参数,使图中的元素紧贴图形边缘。不幸的是,在这种自动化中没有考虑图例,但我们可以提供一个矩形框,整个子图区域(包括标签)将适合。

    plt.tight_layout(rect=[0,0,0.75,1])
    
  • 使用bbox_inches = "tight" 保存数字 参数bbox_inches = "tight"plt.savefig可以用来保存图形,使画布上的所有艺术家(包括图例)都适合保存的区域。如果需要,可自动调整图形大小。

    plt.savefig("output.png", bbox_inches="tight")
    
  • 自动调整子情节参数
    在此答案中可以找到一种自动调整子图位置以使图例适合画布而不更改图形大小的方法:Creating figure with exact size and no padding (and legend outside the axes)

上述案例之间的比较:

enter image description here

替代

数字图例
可以使用图例而不是轴matplotlib.figure.Figure.legend使用图例。这对于matplotlib版本&gt; = 2.1特别有用,其中不需要特殊参数

fig.legend(loc=7) 

为图中不同轴的所有艺术家创建一个图例。使用loc参数放置图例,类似于它放置在轴内的方式,但是参考整个图形 - 因此它会稍微自动地在轴外。剩下的就是调整子图,使图例和轴之间没有重叠。这里的点&#34;从上面调整子图参数&#34; 会很有帮助。一个例子:

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()从真实图中获取,然后可以提供给{{1}中的图例} subplot,lax。下面是一个完整的例子。

lax.legend(h,l)

这会产生一个视觉上非常类似于上图的情节:

enter image description here

我们也可以使用第一个轴放置图例,但使用图例轴的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()

bbox_transform

在这种方法中,我们不需要从外部获取图例句柄,但我们需要指定ax.legend(bbox_to_anchor=(0,0,1,1), bbox_transform=lax.transAxes) lax.axis("off") 参数。

进一步阅读和注释:

  • 考虑一下matplotlib legend guide以及一些你想用传说做的其他事情的例子。
  • 在回答这个问题时,可以直接找到一些用于为饼图放置图例的示例代码:Python - Legend overlaps with the pie chart
  • bbox_to_anchor参数可以使用数字而不是字符串,这会使调用更短,但是,它们不是非常直观地相互映射。以下是参考的映射:

enter image description here

答案 2 :(得分:130)

只需在legend()电话后拨打plot()来电:

# 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

答案 3 :(得分:86)

创建字体属性

from matplotlib.font_manager import FontProperties

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

答案 4 :(得分:74)

简短回答:您可以使用bbox_to_anchor + bbox_extra_artists + bbox_inches='tight'


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

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

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_inchessavefig在已保存的图像中包含裁剪的元素:

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

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

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 does

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

答案 5 :(得分:58)

要将图例放置在绘图区域之外,请使用loc的{​​{1}}和bbox_to_anchor个关键字。例如,以下代码将图例放置在绘图区域的右侧:

legend()

有关详细信息,请参阅legend guide

答案 6 :(得分:54)

简答:在图例上调用draggable并以交互方式将其移动到任意位置:

ax.legend().draggable()

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

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()

答案 7 :(得分:52)

除了此处的所有优秀答案之外,matplotlibpylab的较新版本可以自动确定放置图例的位置,而不会干扰图,如果可能的话。

pylab.legend(loc='best')

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

但是,如果没有地方放置图例而不重叠数据,那么你会想尝试其他一个答案;使用loc="best"永远不会将图例的放在之外。

答案 8 :(得分:13)

不完全是你要求的,但我发现它是同一问题的替代方案。 让传奇半透明,如下: matplotlib plot with semi transparent legend and semitransparent text box

这样做:

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)

答案 9 :(得分:7)

值得刷新这个问题,因为新版本的Matplotlib使得将图例放置在情节之外变得更加容易。我用Matplotlib版本3.1.1制作了这个示例。

用户可以将2元组的坐标传递到loc参数,以将图例放置在边界框中的任何位置。唯一的难题是您需要运行plt.tight_layout()来使matplotlib重新计算绘图尺寸,以便图例可见:

import matplotlib.pyplot as plt

plt.plot([0, 1], [0, 1], label="Label 1")
plt.plot([0, 1], [0, 2], label='Label 2')

plt.legend(loc=(1.05, 0.5))
plt.tight_layout()

这将导致以下情节:

plot with legend outside

参考:

答案 10 :(得分:7)

如上所述,您也可以将图例放置在图中,或稍微偏离边缘。以下是使用Plotly Python APIIPython Notebook示例。我在团队中。

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

import plotly
import math
import random
import numpy as np

然后,安装Plotly:

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移动和重新设置图例和图形的样式。要移动图例,可以使用以下选项通过指定&lt; = 1的x和y值来将图例放置在图表中.E.g:

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

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

enter image description here

答案 11 :(得分:3)

这些方面的东西对我有用。从Joe获取的一些代码开始,此方法修改窗口宽度以自动将图例放在图的右侧。

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()

答案 12 :(得分:2)

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

答案 13 :(得分:1)

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

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()

答案 14 :(得分:1)

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

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')

答案 15 :(得分:1)

这是另一种解决方案,类似于添加bbox_extra_artistsbbox_inches,您不必在savefig电话的范围内拥有额外的艺术家。我想出了这个,因为我在函数中生成了大部分情节。

您可以提前将其添加到Figure的艺术家,而不是将所有添加内容添加到边界框中。使用类似于Franck Dernoncourt的answer above

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")

Here's the generated plot.

答案 16 :(得分:0)

不知道你是否已经解决了你的问题...可能是的,但...... 我只是使用字符串'outside'作为位置,就像在matlab中一样。 我从matplotlib导入了pylab。 请参阅以下代码:

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)

Click to see the plot