我有一系列20个图表(不是子图),需要在一个单独的图中制作。我希望图例在框之外。同时,我不想改变坐标轴,因为图的大小会缩小。
- 我希望保持图例框在绘图区域之外(我想让图例在绘图区域右侧之外)。
- 是否有方法可以减小图例框内文本的字体大小,使得图例框的大小更小?
我有一系列20个图表(不是子图),需要在一个单独的图中制作。我希望图例在框之外。同时,我不想改变坐标轴,因为图的大小会缩小。
有许多方法可以实现您想要的功能。除了Christian Alis和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()
如果我们做同样的事情,但使用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()
同样地,将图例变得更加水平和/或将其放置在图形顶部(我还打开了圆角和简单的阴影):
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()
或者,缩小当前图的宽度,并将图例完全放在图形轴的外部(注意:如果您使用 tight_layout()
,则不要使用 ax.set_position()
:
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()
同样地,垂直缩小图表,并在底部放置水平图例:
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()
请看Matplotlib图例指南。您还可以查看plt.figlegend()
。bbox_to_anchor
)使用loc
参数在坐标轴的边界框内定位图例,例如plt.legend
中的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
参数给出的方向上扩展。例如,
plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left")将图例放置在轴外,以使图例的左上角在轴坐标中的位置为
(1.04, 1)
。mode
和ncols
。
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
的四元组参数,例如 l4
中的参数,请参见 this question。当 mode="expand"
时,将图例在四元组指定的边界框内水平展开。要进行垂直展开,请参见 this question。
有时候,以图形坐标而不是轴坐标指定边界框可能很有用。这在上面的示例 l5
中展示,其中使用了 bbox_transform
参数将图例放置在图形左下角。
将图例放置在轴外部通常会导致不希望出现的情况,即它完全或部分超出图形画布。
解决此问题的方法有:
Adjust the subplot parameters
One can adjust the subplot parameters such, that the axes take less space inside the figure (and thereby leave more space to the legend) by using plt.subplots_adjust
. E.g.,
plt.subplots_adjust(right=0.7)
leaves 30% space on the right-hand side of the figure, where one could place the legend.
Tight layout
Using plt.tight_layout
Allows to automatically adjust the subplot parameters such that the elements in the figure sit tight against the figure edges. Unfortunately, the legend is not taken into account in this automatism, but we can supply a rectangle box that the whole subplots area (including labels) will fit into.
plt.tight_layout(rect=[0, 0, 0.75, 1])
Saving the figure with bbox_inches = "tight"
The argument bbox_inches = "tight"
to plt.savefig
can be used to save the figure such that all artist on the canvas (including the legend) are fit into the saved area. If needed, the figure size is automatically adjusted.
plt.savefig("output.png", bbox_inches="tight")
Automatically adjusting the subplot parameters
A way to automatically adjust the subplot position such that the legend fits inside the canvas without changing the figure size can be found in this answer: Creating figure with exact size and no padding (and legend outside the axes)
图例说明
可以使用图例来替代坐标轴,matplotlib.figure.Figure.legend
。这在Matplotlib 2.1或更高版本中变得特别有用,不需要任何特殊参数。
fig.legend(loc=7)
loc
参数放置图例,类似于在坐标轴内部放置图例,但是参考整个图形 - 因此它将自动位于轴外部。剩下的就是调整子图,以便图例和轴之间没有重叠。这里上面提到的点“调整子图参数”会有所帮助。以下是一个示例: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()
专属子图中的图例
除了使用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 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()
ax.legend(bbox_to_anchor=(0, 0, 1, 1), bbox_transform=lax.transAxes)
lax.axis("off")
像这样,在plot()
之后调用legend()
:
# 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))
结果可能看起来像这样:
FontProperties
中指定set_size
来使图例文本变小。matplotlib.legend
matplotlib.pyplot.legend
matplotlib.font_manager
set_size(self, size)
import matplotlib.pyplot as plt
from 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()
的loc
和bbox_to_anchor
关键字。例如,以下代码将把图例放置在绘图区域的右侧:
legend(loc="upper left", bbox_to_anchor=(1,1))
更多信息请参见图例指南
plt.tight_layout()
函数? - Christian Alis简短回答:您可以使用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')
为了防止图例框被裁剪,当您保存图形时,可以使用参数bbox_extra_artists
和bbox_inches
要求savefig
在保存的图像中包含裁剪的元素:
fig.savefig('image_output.png', bbox_extra_artists=(lgd,), bbox_inches='tight')
示例(只更改了最后一行以向fig.savefig()
添加2个参数):
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')
我希望Matplotlib可以像Matlab一样,原生支持图例框的外部位置:
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])
除了这里的所有优秀答案,更新版本的matplotlib
和pylab
可以自动决定在哪里放置图例而不干扰绘图,如果可能的话。
pylab.legend(loc='best')
如果可能的话,这将自动将图例放置在数据远离的地方!
但是,如果没有可以放置图例而不重叠数据的位置,则需要尝试其他答案;使用 loc="best"
永远不会将图例放在绘图区外部。
from numpy import arange, sin, pi
import matplotlib.pyplot as plt
t = arange(0.0, 100.0, 0.01)
fig = plt.figure(1)
ax1 = fig.add_subplot(211)
ax1.scatter(t, sin(2*pi*t),label='test')
ax1.grid(True)
# ax1.set_ylim((-2, 2))
ax1.set_ylabel('1 Hz')
ax1.set_title('一个正弦波或两个')
for label in ax1.get_xticklabels():
label.set_color('r')
plt.legend(loc='best')
plt.show()
- adam.r简短回答:将可拖动功能应用于图例,然后交互地将其移动到任何位置:
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()
现在,图例可以直接接受“外部”位置,例如loc='outside right upper'
。
只需确保布局已受到限制,然后在loc
字符串前加上“outside”即可:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(layout='constrained')
# --------------------
x = np.linspace(-np.pi, np.pi)
ax.plot(x, x, label='$f(x) = x$')
ax.plot(x, np.sin(x), label='$f(x) = sin(x)$')
ax.plot(x, np.cos(x), label='$f(x) = cos(x)$')
fig.legend(loc='outside right upper')
# -------
plt.show()
多个小图在新的“外部”位置也能正常工作:
fig, (ax1, ax2) = plt.subplots(1, 2, layout='constrained')
# --------------------
x = np.linspace(-np.pi, np.pi)
ax1.plot(x, x, '-', label='$f(x) = x$')
ax1.plot(x, np.sin(x), '--', label='$f(x) = sin(x)$')
ax2.plot(x, np.cos(x), ':', label='$f(x) = cos(x)$')
fig.legend(loc='outside right center')
# -------
当然,“外部”位置是预设的,如果您需要更精细的定位,请使用旧答案。但是标准位置应适用于大多数情况:
注:此处涉及IT技术相关内容,可能需要具备一定专业知识才能更好地理解。
locs = [
'outside upper left', 'outside upper center', 'outside upper right',
'outside center right', 'upper center left',
'outside lower right', 'outside lower center', 'outside lower left',
]
for loc in locs:
fig.legend(loc=loc, title=loc)
locs = [
'outside right upper', 'outside right lower',
'outside left lower', 'outside left upper',
]
for loc in locs:
fig.legend(loc=loc, title=loc)
ax.legend(loc="outside lower left", ncols=3)
)。 - undefinedMatplotlib的更新版本使得在图外部放置图例变得更加容易。我是用Matplotlib3.1.1
版本制作了这个示例。
用户可以通过将一个二元组坐标传递给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()
这导致了以下的图表:
参考文献:
sns.move_legend
,如将seaborn图例移动到不同位置所示。.move_legend
可以传递所有.legend
的参数,并且下面的所有答案都直接适用于seaborn轴级别的绘图(例如返回matplotlib Axes的绘图)。 - Trenton McKinney