如何为多个子图制作一个图例?

2025-01-07 08:44:00
admin
原创
108
摘要:问题描述:我正在用 Matplotlib 绘制相同类型的信息,但针对不同的国家/地区,并绘制多个子图。也就是说,我在 3x3 网格上有九个图,所有图的线条都相同(当然,每条线的值不同)。但是,我还没有弄清楚如何将单个图例(因为所有九个子图都有相同的线条)只放在图形上一次。我该如何做呢?解决方案 1:您还可以在...

问题描述:

我正在用 Matplotlib 绘制相同类型的信息,但针对不同的国家/地区,并绘制多个子图。也就是说,我在 3x3 网格上有九个图,所有图的线条都相同(当然,每条线的值不同)。

但是,我还没有弄清楚如何将单个图例(因为所有九个子图都有相同的线条)只放在图形上一次。

我该如何做呢?


解决方案 1:

您还可以在最后一个轴上调用一个很好的函数get_legend_handles_labels()(如果您对它们进行迭代),它将从label=参数中收集您需要的所有内容:

handles, labels = ax.get_legend_handles_labels()
fig.legend(handles, labels, loc='upper center')

如果pyplot正在使用接口而不是Axes接口,请使用:

handles, labels = plt.gca().get_legend_handles_labels()

要从子图中删除图例,请参阅删除 matplotlib 图形上的图例。

要合并twinx图例,请参阅使用 twinx() 的次轴:如何添加到图例。

解决方案 2:

figlegend可能是你正在寻找的:matplotlib.pyplot.figlegend

一个例子是图例演示

另一个例子:

plt.figlegend(lines, labels, loc = 'lower center', ncol=5, labelspacing=0.)

或者:

fig.legend(lines, labels, loc = (0.5, 0), ncol=5)

解决方案 3:

总结

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()
  • 如果您想坚持使用官方的 Matplotlib API,那么这是完美的,否则请参阅下面的注释 1(有一个私有
    方法......)

  • 这两条线

lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes]
lines, labels = [sum(lol, []) for lol in zip(*lines_labels)]

值得解释,请参阅下文注释2。

  • 我尝试了最受欢迎和接受的答案提出的方法,

 # fig.legend(lines, labels, loc='upper center', ncol=4)
 fig.legend(*a2.get_legend_handles_labels(),
            loc='upper center', ncol=4)

这就是我得到的

在此处输入图片描述


注 1

如果您不介意使用matplotlib.legend模块的私有方法...这真的要容易得多

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 和效率?;-)

解决方案 4:

对于具有多个轴的 中的单个图例的自动定位figure,就像使用 获得的图例一样subplots(),以下解决方案非常有效:

plt.legend(lines, labels, loc = 'lower center', bbox_to_anchor = (0, -0.1, 1, 1),
           bbox_transform = plt.gcf().transFigure)

使用bbox_to_anchorbbox_transform=plt.gcf().transFigure,您将定义一个大小为 的新边界框figure作为 的参考loc。使用(0, -0.1, 1, 1)将此边界框略微向下移动,以防止图例被放置在其他艺术家之上。

OBS:使用fig.set_size_inches()和使用使用此解决方案fig.tight_layout()

解决方案 5:

您只需要在循环之外询问一次图例即可。

例如,在这种情况下,我有 4 个子图,具有相同的线条和一个图例。

from matplotlib.pyplot import *

ficheiros = ['120318.nc', '120319.nc', '120320.nc', '120321.nc']

fig = figure()
fig.suptitle('concentration profile analysis')

for a in range(len(ficheiros)):
    # dados is here defined
    level = dados.variables['level'][:]

    ax = fig.add_subplot(2,2,a+1)
    xticks(range(8), ['0h','3h','6h','9h','12h','15h','18h','21h']) 
    ax.set_xlabel('time (hours)')
    ax.set_ylabel('CONC ($mu g. m^{-3}$)')

    for index in range(len(level)):
        conc = dados.variables['CONC'][4:12,index] * 1e9
        ax.plot(conc,label=str(level[index])+'m')

    dados.close()

ax.legend(bbox_to_anchor=(1.05, 0), loc='lower left', borderaxespad=0.)
         # it will place the legend on the outer right-hand side of the last axes

show()

解决方案 6:

在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,
)

解决方案 7:

如果您使用条形图的子图,并且每个条形使用不同的颜色,则使用 自己创建工件可能会更快mpatches

假设您有四个不同颜色的条形图,分别为rmck,您可以按如下方式设置图例:

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

解决方案 8:

使用 Matplotlib 2.2.2,可以使用 gridspec 功能实现这一点。

在下面的示例中,目标是将四个子图以 2x2 的方式排列,并在底部显示图例。在底部创建一个“假”轴,以将图例放置在固定位置。然后关闭“假”轴,只显示图例。结果:

Matplotlib 生成的一些图表

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

# Gridspec demo
fig = plt.figure()
fig.set_size_inches(8, 9)
fig.set_dpi(100)

rows   = 17 # The larger the number here, the smaller the spacing around the legend
start1 = 0
end1   = int((rows-1)/2)
start2 = end1
end2   = int(rows-1)

gspec = gridspec.GridSpec(ncols=4, nrows=rows)

axes = []
axes.append(fig.add_subplot(gspec[start1:end1, 0:2]))
axes.append(fig.add_subplot(gspec[start2:end2, 0:2]))
axes.append(fig.add_subplot(gspec[start1:end1, 2:4]))
axes.append(fig.add_subplot(gspec[start2:end2, 2:4]))
axes.append(fig.add_subplot(gspec[end2, 0:4]))

line, = axes[0].plot([0, 1], [0, 1], 'b')         # Add some data
axes[-1].legend((line,), ('Test',), loc='center') # Create legend on bottommost axis
axes[-1].set_axis_off()                           # Don't show the bottom-most axis

fig.tight_layout()
plt.show()

解决方案 9:

该答案是对用户707650关于图例位置的答案的补充。

我第一次尝试用户 707650 的解决方案由于图例和子图标题重叠而失败。

事实上,重叠是由 引起的fig.tight_layout(),它改变了子图的布局,而没有考虑图例。但是,fig.tight_layout()是必要的。

为了避免重叠,我们可以fig.tight_layout()通过 来为图形的图例留出空间fig.tight_layout(rect=(0,0,1,0.9))

tight_layout() 参数的描述。

解决方案 10:

在我的编码历程中,所有先前的答案都超出了我的理解范围,所以我只添加了另一个称为补丁的 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中的同一个单元格中)。

结果

(我把这些名字改成了我自己的传奇。)

我改掉了自己的名字

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用