如何将传奇置于剧情之外

我有一系列20个情节(不是子情节)要在一个图形中制作。我希望图例在盒子外面。同时,我不想改变轴,因为图形的大小变小了。

  1. 我想把图例框放在情节区域之外(我希望图例在情节区域的右侧)。
  2. 有没有办法减少图例框内文字的字体大小,使图例框的大小变小?
1499725 次浏览
import matplotlib.pyplot as pltfrom matplotlib.font_manager import FontProperties
fontP = FontProperties()fontP.set_size('xx-small')
p1, = plt.plot([1, 2, 3], label='Line 1')p2, = plt.plot([3, 2, 1], label='Line 2')plt.legend(handles=[p1, p2], title='title', bbox_to_anchor=(1.05, 1), loc='upper left', prop=fontP)

在此处输入图片描述

  • fontsize='xx-small'也可以,不导入FontProperties
plt.legend(handles=[p1, p2], title='title', bbox_to_anchor=(1.05, 1), loc='upper left', fontsize='xx-small')

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

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

有关更多信息,请参阅图例指南

有很多方法可以做你想做的事情。要添加到什么Christian AlisNavi已经说了,您可以使用bbox_to_anchor关键字参数将图例部分放置在轴之外和/或减小字体大小。

在你考虑减小字体大小(这会让事情变得非常难以阅读)之前,试着把图例放在不同的地方:

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

import matplotlib.pyplot as pltimport 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 pltimport 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()

替换文本

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

import matplotlib.pyplot as pltimport 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

或者,缩小当前图的宽度,并将图例完全放在图的轴线之外(注意:如果使用#0,则省略ax.set_position()

import matplotlib.pyplot as pltimport 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 axisax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.show()

替换文本

以类似的方式,垂直缩小情节,并在底部放置一个水平图例:

import matplotlib.pyplot as pltimport 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 bottombox = 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 axisax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),fancybox=True, shadow=True, ncol=5)
plt.show()

替换文本

看看matplotlib图例指南。你也可以看看#0

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

简短回答:在图例上调用draggable并以交互方式将其移动到您想要的任何位置:

ax.legend().draggable()

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

import matplotlib.pylab as pltimport numpy as np#define the figure and get an axes instancefig = plt.figure()ax = fig.add_subplot(111)#plot the datax = 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()

从Joe的一些代码开始,此方法修改窗口宽度以自动适应图形右侧的图例。

import matplotlib.pyplot as pltimport 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 axisleg = 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().widthwinWidthNew = w*dpi+legWidthfig.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/winWidthNewx0 = xlocs[0]*factorx1 = xlocs[1]*factorwidth = box.width*factorax.set_position([x0,ylocs[0],x1-x0,ylocs[1]-ylocs[0]])
plt.draw()

如前所述,您也可以将图例放在情节中,或者稍微将其放在边缘。这是一个使用Python API的示例,由IPython笔记本制作。我是团队成员。

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

import plotlyimport mathimport randomimport numpy as np

然后,安装Ploly:

un='IPython.Demo'k='1fw3zw2o13'py = plotly.plotly(username=un, key=k)

def sin(x,n):sine = 0for i in range(n):sign = (-1)**isine = sine + ((x**(2.0*i+1))/math.factorial(2*i+1))*signreturn 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)

这将创建您的图表,并允许您将图例保留在绘图本身中。如果未设置图例,则默认将其放置在绘图中,如下所示。

输入图片描述

对于替代位置,您可以紧密对齐图表的边缘和图例的边框,并删除边界线以更接近。

输入图片描述

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

  • {"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},也在留档中描述:

输入图片描述

这不是你所要求的,但我发现这是同一个问题的替代方案。

使图例半透明,如下所示:

带有半透明图例和半透明输入框的Matplotlib图

这样做:

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 boxax.text(0.02, 0.02, yourstring, verticalalignment='bottom',horizontalalignment='left',fontsize=10,bbox={'facecolor':'white', 'alpha':0.6, 'pad':10},transform=self.ax.transAxes)

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

import matplotlib.pyplot as pltp1, = 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()

只需在plot()调用后调用legend(),如下所示:

# Matplotlibplt.plot(...)plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
# Pandasdf.myCol.plot().legend(loc='center left', bbox_to_anchor=(1, 0.5))

结果看起来像这样:

在此输入图片描述

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


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

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

import matplotlib.pyplot as plt
# dataall_x = [10,20,30]all_y = [[1,3], [1.5,2.9],[3,2]]
# Plotfig = plt.figure(1)ax = fig.add_subplot(111)ax.plot(all_x, all_y)
# Add legend, title and axis labelslgd = 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_artistsbbox_inches要求savefig在保存的图像中包含裁剪的元素:

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

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

import matplotlib.pyplot as plt
# dataall_x = [10,20,30]all_y = [[1,3], [1.5,2.9],[3,2]]
# Plotfig = plt.figure(1)ax = fig.add_subplot(111)ax.plot(all_x, all_y)
# Add legend, title and axis labelslgd = 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')

输入图片描述

我希望matplotlib本机允许图例框的外部位置为matlab做

figurex = 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 brownset(hleg,'FontAngle','italic','TextColor',[.3,.2,.1])

输入图片描述

除了这里的所有优秀答案之外,如果可能的话,更新版本的matplotlibpylab可以自动确定在不干扰情节的情况下放置图例的位置

pylab.legend(loc='best')

这将自动放置图例远离数据,如果可能的话!

比较loc='最佳'的使用

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

当我有一个巨大的传奇时,对我有效的解决方案是使用一个额外的空图像布局。

在下面的例子中,我做了四行,并在底部绘制了带有图例偏移量的图像(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')

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

您可以提前将它们添加到Figure的艺术家中,而不是在您想写出来时将所有添加到边界框中。使用类似于弗兰克·德农考特的回答的东西:

import matplotlib.pyplot as plt
# Dataall_x = [10, 20, 30]all_y = [[1, 3], [1.5, 2.9], [3, 2]]
# Plotting functiondef 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 changeax.set_title("Title")ax.set_xlabel("x label")ax.set_ylabel("y label")return fig
# Plottingfig = gen_plot(all_x, all_y)
# No need for `bbox_extra_artists`fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")

这是生成的图

我只是使用字符串'center left'作为位置,就像MATLAB一样。

我从Matplotlib导入了pylab。

代码如下:

from matplotlib as pltfrom 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(loc='center left', bbox_to_anchor=(1, 0.5), fancybox = True, shadow = True)

在此输入图片描述

放置图例(bbox_to_anchor

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

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

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

一种更通用的方法是使用#0参数手动指定应该放置图例的边界框。可以限制自己只提供bbox的(x0, y0)部分。这会创建一个零跨度框,图例将在其外按loc参数给出的方向扩展。例如,

plt.legend(bbox_to_anchor=(1.04,1), loc="左上角")

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

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

在此处输入图片描述

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

有关如何将4元组参数解释为bbox_to_anchor的详细信息,请参阅这个问题mode="expand"在4元组给出的边界框内水平扩展图例。关于垂直扩展的图例,请参阅这个问题

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

后处理

将图例放置在轴之外通常会导致不希望的情况,即它完全或部分位于图形画布之外。

这个问题的解决办法是:

  • 调整子图参数
    可以通过使用#0调整子图参数,使得轴在图形中占用更少的空间(从而为图例留下更多空间)。例如,

    plt.subplots_adjust(right=0.7)

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

  • 紧凑的布局
    使用#0允许自动调整子图参数,使得图形中的元素紧靠图形边缘。不幸的是,这个自动性没有考虑到图例,但是我们可以提供一个矩形框,整个子图区域(包括标签)都可以放入。

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

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

上面讨论的案例之间的比较:

在此处输入图片描述

替代品

一个传奇人物

可以对图形使用图例而不是轴,#0。这对于Matplotlib版本2.1或更高版本特别有用,其中不需要特殊参数

fig.legend(loc=7)

在图形的不同轴上为所有艺术家创建一个图例。图例使用loc参数放置,类似于它放置在轴内的方式,但引用整个图形-因此它将在某种程度上自动位于轴外。剩下的是调整子情节,使图例和轴之间没有重叠。上面的第1点将很有帮助。一个例子:

import numpy as npimport 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()

在此处输入图片描述

专用子情节轴内的传说

使用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)中提供给图例。下面是一个完整的例子。

import matplotlib.pyplot as pltplt.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")

在这种方法中,我们不需要从外部获取图例句柄,但我们需要指定bbox_to_anchor参数。

更多阅读和笔记:

  • 考虑Matplotlib图例指南和一些你想用图例做的其他事情的例子。
  • 一些用于放置饼图图例的示例代码可以直接在这个问题的答案中找到:Python-图例与饼图重叠
  • loc参数可以采用数字而不是字符串,这使得调用更短,但是,它们不是很直观地相互映射。这是供参考的映射:

在此处输入图片描述

较新版本的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()

这导致了以下情节:

外面有图例的情节

参考文献: