如何使用matplotlib为许多子图创建单个图例?

时间:2021-03-29 03:58:33

I am plotting the same type of information, but for different countries, with multiple subplots with matplotlib. That is, I have 9 plots on a 3x3 grid, all with the same for lines (of course, different values per line).

我正在绘制相同类型的信息,但对于不同的国家,使用matplotlib的多个子图。也就是说,我在3x3网格上有9个图,所有线都相同(当然,每行不同的值)。

However, I have not figured out how to put a single legend (since all 9 subplots have the same lines) on the figure just once.

但是,我还没想出如何在图上只放一个图例(因为所有9个子图都有相同的线)。

How do I do that?

我怎么做?

6 个解决方案

#1


76  

figlegend may be what you're looking for: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend

figlegend可能是您正在寻找的:http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend

Example here: http://matplotlib.org/examples/pylab_examples/figlegend_demo.html

示例:http://matplotlib.org/examples/pylab_examples/figlegend_demo.html

Another example:

另一个例子:

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

or:

要么:

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

#2


21  

There is also a nice function get_legend_handles_labels() you can call on the last axis (if you iterate over them) that would collect everything you need from label= arguments:

还有一个很好的函数get_legend_handles_labels(),你可以调用最后一个轴(如果你迭代它们),它们将从label = arguments收集你需要的一切:

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

#3


14  

You just have to ask for the legend once, outside of your loop.

你只需要在循环之外询问一次传奇。

For example, in this case I have 4 subplots, with the same lines, and a single legend.

例如,在这种情况下,我有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()

#4


13  

For the automatic positioning of a single legend in a figure with many axes, like those obtained with subplots(), the following solution works really well:

对于具有多个轴的图形中的单个图例的自动定位,如使用子图()获得的那些,以下解决方案非常有效:

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

With bbox_to_anchor and bbox_transform=plt.gcf().transFigure you are defining a new bounding box of the size of your figureto be a reference for loc. Using (0,-0.1,1,1) moves this bouding box slightly downwards to prevent the legend to be placed over other artists.

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

OBS: use this solution AFTER you use fig.set_size_inches() and BEFORE you use fig.tight_layout()

OBS:使用fig.set_size_inches()后使用此解决方案,然后使用fig.tight_layout()

#5


1  

While rather late to the game, I'll give another solution here as this is still one of the first links to show up on google. Using matplotlib 2.2.2, this can be achieved using the gridspec feature. In the example below the aim is to have four subplots arranged in a 2x2 fashion with the legend shown at the bottom. A 'faux' axis is created at the bottom to place the legend in a fixed spot. The 'faux' axis is then turned off so only the legend shows. Result: https://i.stack.imgur.com/5LUWM.png.

虽然比较晚,我会在这里给出另一个解决方案,因为这仍然是第一个出现在谷歌上的链接之一。使用matplotlib 2.2.2,可以使用gridspec功能实现。在下面的示例中,目标是以2x2方式排列四个子图,底部显示图例。底部会创建一个“虚假”轴,以将图例放在固定的位置。然后关闭'仿'轴,只有图例显示。结果:https://i.stack.imgur.com/5LUWM.png。

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 bottommost axis

fig.tight_layout()
plt.show()

#6


0  

This answer is a complement to @Evert's on the legend position.

这个答案是对@Evert's传奇位置的补充。

My first try on @Evert's solution failed due to overlaps of the legend and the subplot's title.

由于图例和子图标题的重叠,我对@Evert的解决方案的第一次尝试失败了。

In fact, the overlaps are caused by fig.tight_layout(), which changes the subplots' layout without considering the figure legend. However, fig.tight_layout() is necessary.

实际上,重叠是由fig.tight_layout()引起的,这会改变子图的布局而不考虑图例。但是,fig.tight_layout()是必要的。

In order to avoid the overlaps, we can tell fig.tight_layout() to leave spaces for the figure's legend by fig.tight_layout(rect=(0,0,1,0.9)).

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

Description of tight_layout() parameters.

tight_layout()参数的描述。

#1


76  

figlegend may be what you're looking for: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend

figlegend可能是您正在寻找的:http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend

Example here: http://matplotlib.org/examples/pylab_examples/figlegend_demo.html

示例:http://matplotlib.org/examples/pylab_examples/figlegend_demo.html

Another example:

另一个例子:

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

or:

要么:

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

#2


21  

There is also a nice function get_legend_handles_labels() you can call on the last axis (if you iterate over them) that would collect everything you need from label= arguments:

还有一个很好的函数get_legend_handles_labels(),你可以调用最后一个轴(如果你迭代它们),它们将从label = arguments收集你需要的一切:

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

#3


14  

You just have to ask for the legend once, outside of your loop.

你只需要在循环之外询问一次传奇。

For example, in this case I have 4 subplots, with the same lines, and a single legend.

例如,在这种情况下,我有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()

#4


13  

For the automatic positioning of a single legend in a figure with many axes, like those obtained with subplots(), the following solution works really well:

对于具有多个轴的图形中的单个图例的自动定位,如使用子图()获得的那些,以下解决方案非常有效:

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

With bbox_to_anchor and bbox_transform=plt.gcf().transFigure you are defining a new bounding box of the size of your figureto be a reference for loc. Using (0,-0.1,1,1) moves this bouding box slightly downwards to prevent the legend to be placed over other artists.

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

OBS: use this solution AFTER you use fig.set_size_inches() and BEFORE you use fig.tight_layout()

OBS:使用fig.set_size_inches()后使用此解决方案,然后使用fig.tight_layout()

#5


1  

While rather late to the game, I'll give another solution here as this is still one of the first links to show up on google. Using matplotlib 2.2.2, this can be achieved using the gridspec feature. In the example below the aim is to have four subplots arranged in a 2x2 fashion with the legend shown at the bottom. A 'faux' axis is created at the bottom to place the legend in a fixed spot. The 'faux' axis is then turned off so only the legend shows. Result: https://i.stack.imgur.com/5LUWM.png.

虽然比较晚,我会在这里给出另一个解决方案,因为这仍然是第一个出现在谷歌上的链接之一。使用matplotlib 2.2.2,可以使用gridspec功能实现。在下面的示例中,目标是以2x2方式排列四个子图,底部显示图例。底部会创建一个“虚假”轴,以将图例放在固定的位置。然后关闭'仿'轴,只有图例显示。结果:https://i.stack.imgur.com/5LUWM.png。

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 bottommost axis

fig.tight_layout()
plt.show()

#6


0  

This answer is a complement to @Evert's on the legend position.

这个答案是对@Evert's传奇位置的补充。

My first try on @Evert's solution failed due to overlaps of the legend and the subplot's title.

由于图例和子图标题的重叠,我对@Evert的解决方案的第一次尝试失败了。

In fact, the overlaps are caused by fig.tight_layout(), which changes the subplots' layout without considering the figure legend. However, fig.tight_layout() is necessary.

实际上,重叠是由fig.tight_layout()引起的,这会改变子图的布局而不考虑图例。但是,fig.tight_layout()是必要的。

In order to avoid the overlaps, we can tell fig.tight_layout() to leave spaces for the figure's legend by fig.tight_layout(rect=(0,0,1,0.9)).

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

Description of tight_layout() parameters.

tight_layout()参数的描述。