matplotlib图例中项目的顺序是如何确定的?

115 投票
7 回答
122749 浏览
提问于 2025-04-17 21:23

我在调整图例中的项目顺序,但我觉得我不应该这样做。我尝试了:

from pylab import *
clf()
ax=gca()
ht=ax.add_patch(Rectangle((1,1),1,1,color='r',label='Top',alpha=.1))
h1=ax.bar(1,2,label='Middle')
hb=ax.add_patch(Rectangle((1,1),1,1,color='k',label='Bottom',alpha=.11))
legend()
show()

结果却是“底部”在“中间”上面。那我该怎么才能得到正确的顺序呢?这个顺序不是应该由创建的顺序决定吗?

代码结果导致图例项目顺序错误

更新:可以使用以下方法来强制调整顺序。我觉得这可能是最简单的方法,但这听起来有点奇怪。问题是,最初的顺序是由什么决定的呢?

hh=[ht,h1,hb]
legend([ht,h1.patches[0],hb],[H.get_label() for H in hh])

7 个回答

7

根据Ian Hincks的回答,我们可以用一行代码来改变图例元素的顺序,这个方法使用了嵌套的列表推导式。这样做的好处是不用给中间变量命名,也减少了代码的重复。

plt.legend(*(
    [ x[i] for i in [2,1,0] ]
    for x in plt.gca().get_legend_handles_labels()
), handletextpad=0.75, loc='best')

我在最后加了一些额外的参数,目的是为了说明plt.legend()这个函数不需要单独调用来格式化和排序元素。

14

下面这个函数可以让你轻松控制图例的顺序,而且看起来也很清晰。

你可以通过标签来指定你想要的顺序。它会找到图例的句柄和标签,去掉重复的标签,并根据你给出的列表(order)进行排序或部分排序。用法如下:

reorderLegend(ax,['Top', 'Middle', 'Bottom'])

具体细节在下面。

#  Returns tuple of handles, labels for axis ax, after reordering them to conform to the label order `order`, and if unique is True, after removing entries with duplicate labels.
def reorderLegend(ax=None,order=None,unique=False):
    if ax is None: ax=plt.gca()
    handles, labels = ax.get_legend_handles_labels()
    labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0])) # sort both labels and handles by labels
    if order is not None: # Sort according to a given list (not necessarily complete)
        keys=dict(zip(order,range(len(order))))
        labels, handles = zip(*sorted(zip(labels, handles), key=lambda t,keys=keys: keys.get(t[0],np.inf)))
    if unique:  labels, handles= zip(*unique_everseen(zip(labels,handles), key = labels)) # Keep only the first of each handle
    ax.legend(handles, labels)
    return(handles, labels)


def unique_everseen(seq, key=None):
    seen = set()
    seen_add = seen.add
    return [x for x,k in zip(seq,key) if not (k in seen or seen_add(k))]
 

这个函数的更新版本在 cpblUtilities.mathgraph 中,链接是 https://gitlab.com/cpbl/cpblUtilities/blob/master/mathgraph.py

使用方法如下:

fig, ax = plt.subplots(1)
ax.add_patch(Rectangle((1,1),1,1,color='r',label='Top',alpha=.1))
ax.bar(1,2,label='Middle')
ax.add_patch(Rectangle((.8,.5),1,1,color='k',label='Bottom',alpha=.1))
legend()


reorderLegend(ax,['Top', 'Middle', 'Bottom'])
show()

可选的 unique 参数可以确保去掉那些标签相同的重复绘图对象。

重新排序标签后的图

104

这里有一个简单的代码片段,用来对图例中的条目进行排序。它假设你已经添加了带标签的绘图元素,比如像下面这样的:

ax.plot(..., label='label1')
ax.plot(..., label='label2')

然后就是主要的部分:

handles, labels = ax.get_legend_handles_labels()
# sort both labels and handles by labels
labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0]))
ax.legend(handles, labels)

这只是对在 http://matplotlib.org/users/legend_guide.html 上列出的代码进行的简单调整。

126

这是对其他一些回答的一个小变化。列表 order 的长度应该和图例项的数量一样,并且手动指定新的顺序。

handles, labels = plt.gca().get_legend_handles_labels()
order = [0,2,1]
plt.legend([handles[idx] for idx in order],[labels[idx] for idx in order])
15

这个顺序是确定的,但里面的一些私有部分可以随时改变。你可以查看这段代码 这里,它会链接到 这里,最后又到 这里。这些“孩子”就是你添加的图形元素,因此句柄列表是按照它们被添加的顺序来排序的(这是在mpl34或mpl35版本中的一个行为变化)。

如果你想明确控制图例中元素的顺序,可以像你编辑时那样,先准备好一个句柄和标签的列表。

撰写回答