Python 如何定位和对齐 matplotlib 图形图例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13894345/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 09:52:46  来源:igfitidea点击:

How to position and align a matplotlib figure legend?

pythonmatplotliblegend

提问by maschu

I have a figure with two subplots as 2 rows and 1 column. I can add a nice looking figure legend with

我有一个带有两个子图的图,分别为 2 行和 1 列。我可以添加一个漂亮的人物图例

fig.legend((l1, l2), ['2011', '2012'], loc="lower center", 
           ncol=2, fancybox=True, shadow=True, prop={'size':'small'})

However, this legend is positioned at the center of the figureand not below the center of the axesas I would like to have it. Now, I can obtain my axes coordinates with

但是,这个图例位于图形的中心,而不是我想要的中心下方。现在,我可以获得我的轴坐标

axbox = ax[1].get_position()

and in theory I should be able to position the legend by specifying the lockeyword with a tuple:

从理论上讲,我应该能够通过使用元组指定loc关键字来定位图例:

fig.legend(..., loc=(axbox.x0+0.5*axbox.width, axbox.y0-0.08), ...)

This works, exceptthat the legend is left aligned so that locspecifies the left edge/corner of the legend box and not the center. I searched for keywords such as align, horizontalalignment, etc., but couldn't find any. I also tried to obtain the "legend position", but legend doesn't have a *get_position()* method. I read about *bbox_to_anchor* but cannot make sense of it when applied to a figure legend. This seems to be made for axes legends.

这是有效的,除了图例左对齐,以便loc指定图例框的左边缘/角而不是中心。我搜索了alignhorizo​​ntalalignment等关键字,但找不到任何关键字。我也尝试获取“图例位置”,但图例没有 *get_position()* 方法。我读过关于 *bbox_to_anchor* 的内容,但在应用于图形图例时无法理解它。这似乎是为轴图例制作的。

Or: should I use a shifted axes legend instead? But then, why are there figure legends in the first place? And somehow it must be possible to "center align" a figure legend, because loc="lower center"does it too.

或者:我应该改用移动轴图例吗?但话说回来,为什么会有人物传说呢?并且以某种方式必须可以“居中对齐”图形图例,因为loc="lower center"也可以这样做。

Thanks for any help,

谢谢你的帮助,

Martin

马丁

采纳答案by Joe Kington

In this case, you can either use axes for figure legendmethods. In either case, bbox_to_anchoris the key. As you've already noticed bbox_to_anchorspecifies a tuple of coordinates (or a box) to place the legend at. When you're using bbox_to_anchorthink of the locationkwarg as controlling the horizontal and vertical alignment.

在这种情况下,您可以将轴用于图形legend方法。无论哪种情况,bbox_to_anchor都是关键。正如您已经注意到的,bbox_to_anchor指定了一个坐标元组(或一个框)来放置图例。当您使用时bbox_to_anchor,可以将locationkwarg 视为控制水平和垂直对齐。

The difference is just whether the tuple of coordinates is interpreted as axes or figure coordinates.

区别仅在于坐标元组是否被解释为轴或图形坐标。

As an example of using a figure legend:

作为使用图形图例的示例:

import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)

x = np.linspace(0, np.pi, 100)

line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

# The key to the position is bbox_to_anchor: Place it at x=0.5, y=0.5
# in figure coordinates.
# "center" is basically saying center horizontal alignment and 
# center vertical alignment in this case
fig.legend([line1, line2], ['yep', 'nope'], bbox_to_anchor=[0.5, 0.5], 
           loc='center', ncol=2)

plt.show()

enter image description here

在此处输入图片说明

As an example of using the axes method, try something like this:

作为使用轴方法的示例,请尝试以下操作:

import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)

x = np.linspace(0, np.pi, 100)

line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

# The key to the position is bbox_to_anchor: Place it at x=0.5, y=0
# in axes coordinates.
# "upper center" is basically saying center horizontal alignment and 
# top vertical alignment in this case
ax1.legend([line1, line2], ['yep', 'nope'], bbox_to_anchor=[0.5, 0], 
           loc='upper center', ncol=2, borderaxespad=0.25)

plt.show()

enter image description here

在此处输入图片说明

回答by Fei Yao

This is a very good question and the accepted answer indicates the key (i.e. locdenotes alignment and bbox_to_anchordenotes position). I have also tried some codes and would like to stress the importance of bbox_transformpropertythat may sometimes needs to be explicitly specified to achieve desired effects. Below I will show you my findings on fig.legend. ax.legendshould be very similar as locand bbox_to_anchorworks the same way.

这是一个很好的问题,接受的答案表示关键(即loc表示对齐和bbox_to_anchor表示位置)。我也尝试了一些代码,并想强调属性的重要性,bbox_transform有时可能需要明确指定才能达到预期的效果。下面我将向您展示我对fig.legend. ax.legend应该非常相似loc并且bbox_to_anchor以相同的方式工作。

When using the default setting, we will have the following.

使用默认设置时,我们将有以下内容。

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,4), sharex=True)

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)

enter image description here

在此处输入图片说明

This is basically satisfactory. But it could be easily found that the legend overlays with the x-axis ticklabels of ax2. This is the problem that will become even severe when figsizeand/or dpiof the figure changes, see the following.

这基本上是令人满意的。但是很容易发现图例与 x 轴刻度标签重叠ax2。这是当figsize和/或dpi图形变化时会变得更加严重的问题,请参见以下内容。

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)

enter image description here

在此处输入图片说明

So you see there are big gaps between ax2and the legend. That's not what we want. Like the questioner, we would like to manually control the location of the legend. First, I will use the 2-number style of bbox_to_anchorlike the answer did.

所以你看ax2和传说之间有很大的差距。那不是我们想要的。像提问者一样,我们想手动控制图例的位置。首先,我将使用bbox_to_anchor像答案一样的 2-number 样式。

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

axbox = ax2.get_position()

# to place center point of the legend specified by loc at the position specified by bbox_to_anchor.
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
           bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05])

enter image description here

在此处输入图片说明

Almost there! But it is totally wrong as the center of the legend is not at the center of what we really mean! The key to solving this is that we need to explicitly inform the bbox_transformas fig.transFigure. By default None, the Axes' transAxes transform will be used. This is understandable as most of the time we will use ax.legend().

差不多好了!但这是完全错误的,因为传说的中心不是我们真正意思的中心!解决这个问题的关键是我们需要明确地通知bbox_transformas fig.transFigure默认为 None,将使用 Axes 的 transAxes 变换。这是可以理解的,因为我们大部分时间都会使用ax.legend().

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

axbox = ax2.get_position()

# to place center point of the legend specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
           bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05], bbox_transform=fig.transFigure)

enter image description here

在此处输入图片说明

As an alternative, we can also use a 4-number style bbox_to_anchorfor loc. This is essentially specify a real box for the legend and locreally denotes alignment! The default bbox_to_anchorshould just be [0,0,1,1], meaning the entire figure box! The four numbers represent x0,y0,width,height, respectively. It is very similar to specifying a cax for a shared colorbar! Hence you can easily change the y0just a little bit lower than axbox.y0and adjust locaccordingly.

作为替代方案,我们也可以使用一个4位数的风格bbox_to_anchorloc。这实质上是为图例指定一个真实的框,并loc真正表示对齐!默认值bbox_to_anchor应该是[0,0,1,1],意思是整个图形框!四个数字x0,y0,width,height分别代表。这与为共享颜色条指定 cax非常相似!因此,您可以轻松地将其更改为y0略低于axbox.y0并进行相应调整loc

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

axbox = ax2.get_position()

# to place center point specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2,
           bbox_to_anchor=[0, axbox.y0-0.05,1,1], bbox_transform=fig.transFigure)

enter image description here

在此处输入图片说明