我绘制了相同类型的信息,但针对不同的国家,使用Matplotlib绘制了多个子图。也就是说,我在一个3x3网格上有9个图,所有的线都是相同的(当然,每条线的值不同)。
然而,我还没有弄清楚如何将一个图例(因为所有九个子图都有相同的线条)放在图形上一次。
我怎么做呢?
我绘制了相同类型的信息,但针对不同的国家,使用Matplotlib绘制了多个子图。也就是说,我在一个3x3网格上有9个图,所有的线都是相同的(当然,每条线的值不同)。
然而,我还没有弄清楚如何将一个图例(因为所有九个子图都有相同的线条)放在图形上一次。
我怎么做呢?
当前回答
博士TL;
lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes]
lines, labels = [sum(lol, []) for lol in zip(*lines_labels)]
fig.legend(lines, labels)
我注意到,其他答案都没有显示一个图像,其中一个图例引用了不同子图中的许多曲线,所以我必须给你看一个……为了让你好奇……
现在,如果我已经戏弄你们够多了,这就是代码
from numpy import linspace
import matplotlib.pyplot as plt
# each Axes has a brand new prop_cycle, so to have differently
# colored curves in different Axes, we need our own prop_cycle
# Note: we CALL the axes.prop_cycle to get an itertoools.cycle
color_cycle = plt.rcParams['axes.prop_cycle']()
# I need some curves to plot
x = linspace(0, 1, 51)
functs = [x*(1-x), x**2*(1-x),
0.25-x*(1-x), 0.25-x**2*(1-x)]
labels = ['$x-x²$', '$x²-x³$',
'$\\frac{1}{4} - (x-x²)$', '$\\frac{1}{4} - (x²-x³)$']
# the plot,
fig, (a1,a2) = plt.subplots(2)
for ax, f, l, cc in zip((a1,a1,a2,a2), functs, labels, color_cycle):
ax.plot(x, f, label=l, **cc)
ax.set_aspect(2) # superfluos, but nice
# So far, nothing special except the managed prop_cycle. Now the trick:
lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes]
lines, labels = [sum(lol, []) for lol in zip(*lines_labels)]
# Finally, the legend (that maybe you'll customize differently)
fig.legend(lines, labels, loc='upper center', ncol=4)
plt.show()
If you want to stick with the official Matplotlib API, this is perfect, otherwise see note no.1 below (there is a private method...) The two lines lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes] lines, labels = [sum(lol, []) for lol in zip(*lines_labels)] deserve an explanation, see note 2 below. I tried the method proposed by the most up-voted and accepted answer, # fig.legend(lines, labels, loc='upper center', ncol=4) fig.legend(*a2.get_legend_handles_labels(), loc='upper center', ncol=4) and this is what I've got
注1 如果您不介意使用matplotlib的私有方法。传说模块…这真的非常非常简单
from matplotlib.legend import _get_legend_handles_labels
...
fig.legend(*_get_legend_handles_and_labels(fig.axes), ...)
注2
我把这两行棘手的代码封装在一个函数中,只有四行代码,但是注释了很多
def fig_legend(fig, **kwdargs):
# Generate a sequence of tuples, each contains
# - a list of handles (lohand) and
# - a list of labels (lolbl)
tuples_lohand_lolbl = (ax.get_legend_handles_labels() for ax in fig.axes)
# E.g., a figure with two axes, ax0 with two curves, ax1 with one curve
# yields: ([ax0h0, ax0h1], [ax0l0, ax0l1]) and ([ax1h0], [ax1l0])
# The legend needs a list of handles and a list of labels,
# so our first step is to transpose our data,
# generating two tuples of lists of homogeneous stuff(tolohs), i.e.,
# we yield ([ax0h0, ax0h1], [ax1h0]) and ([ax0l0, ax0l1], [ax1l0])
tolohs = zip(*tuples_lohand_lolbl)
# Finally, we need to concatenate the individual lists in the two
# lists of lists: [ax0h0, ax0h1, ax1h0] and [ax0l0, ax0l1, ax1l0]
# a possible solution is to sum the sublists - we use unpacking
handles, labels = (sum(list_of_lists, []) for list_of_lists in tolohs)
# Call fig.legend with the keyword arguments, return the legend object
return fig.legend(handles, labels, **kwdargs)
我认识到sum(list_of_lists,[])是一个非常低效的方法来扁平化列表的列表,但①我喜欢它的紧凑性,②通常是几个子图中的几条曲线,③Matplotlib和效率?: -)
其他回答
Figlegend可能就是您要找的:matplotlib.pyplot.figlegend
一个例子是在图图例演示。
另一个例子:
plt.figlegend(lines, labels, loc = 'lower center', ncol=5, labelspacing=0.)
Or:
fig.legend(lines, labels, loc = (0.5, 0), ncol=5)
如果您正在使用柱状图的子图,每个柱状图都有不同的颜色,那么使用mpatch自己创建工件可能会更快。
假设你有四个不同颜色的条,分别是r、m、c和k,你可以这样设置图例:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
labels = ['Red Bar', 'Magenta Bar', 'Cyan Bar', 'Black Bar']
#####################################
# Insert code for the subplots here #
#####################################
# Now, create an artist for each color
red_patch = mpatches.Patch(facecolor='r', edgecolor='#000000') # This will create a red bar with black borders, you can leave out edgecolor if you do not want the borders
black_patch = mpatches.Patch(facecolor='k', edgecolor='#000000')
magenta_patch = mpatches.Patch(facecolor='m', edgecolor='#000000')
cyan_patch = mpatches.Patch(facecolor='c', edgecolor='#000000')
fig.legend(handles = [red_patch, magenta_patch, cyan_patch, black_patch], labels=labels,
loc="center right",
borderaxespad=0.1)
plt.subplots_adjust(right=0.85) # Adjust the subplot to the right for the legend
所有之前的答案都超出了我的理解,在我的编码旅程的这个状态下,所以我只是添加了另一个Matplotlib方面,称为补丁:
import matplotlib.patches as mpatches
first_leg = mpatches.Patch(color='red', label='1st plot')
second_leg = mpatches.Patch(color='blue', label='2nd plot')
thrid_leg = mpatches.Patch(color='green', label='3rd plot')
plt.legend(handles=[first_leg ,second_leg ,thrid_leg ])
补丁方面把我需要的所有数据放在我的最终图(这是一个线状图,在Jupyter Notebook的同一个单元格中结合了三个不同的线状图)。
结果
(我更改了我自己命名的图例的名称。)
基于gboffi和Ben Usman的回答:
如果在不同的子图中有不同的线,但颜色和标签相同,你可以这样做:
labels_handles = {
label: handle for ax in fig.axes for handle, label in zip(*ax.get_legend_handles_labels())
}
fig.legend(
labels_handles.values(),
labels_handles.keys(),
loc = "upper center",
bbox_to_anchor = (0.5, 0),
bbox_transform = plt.gcf().transFigure,
)
这个答案是对user707650在图例位置上的回答的补充。
我第一次尝试user707650的解决方案失败了,因为图例和副图的标题重叠。
事实上,重叠是由fig.tight_layout()引起的,它改变了子图的布局,而不考虑图形图例。但是,fig.tight_layout()是必要的。
为了避免重叠,我们可以通过fig.tight_layout(rect=(0,0,1,0.9))告诉fig.tight_layout()为图形图例留出空格。
tight_layout()参数的描述。