【问题标题】:Single legend item with two lines具有两行的单个图例项
【发布时间】:2021-08-07 23:21:22
【问题描述】:

我想生成一个自定义 matplotlib 图例,对于每个条目,每个标签都有两行,如下例所示:

根据一些研究,似乎可以简单地提供两个handles' to thefig.legend(handles, labels)` 方法,(以this post 为例)。但是,如以下示例代码所示,这只是将行叠加在一起。

import matplotlib.lines as mlines
import matplotlib.pyplot as plt

blue_line = mlines.Line2D([], [], color='r')
green_line = mlines.Line2D([], [], linestyle='--', color='k')
fig, ax = plt.subplots(figsize=(5, 5))
handles = [(blue_line,green_line)]
labels = ['test'] 
fig.legend(handles=handles, labels=labels, fontsize=20)  

所以,我想我要么需要转换 Line2D 对象之一,要么生成一个包含两行的新 Patch 对象。但是,我不知道如何做到这一点 - 有没有一种简单的方法可以组合两个补丁,还是我错过了组合手柄的技巧?

上下文

如果这对其他人有帮助,上下文是我正在使用here 讨论的技术,这是一个双轴,它被着色以同时显示两个不同的图。但是,这两行具有相同的标签,因此我想将它们组合在一起。

【问题讨论】:

标签: python matplotlib


【解决方案1】:

在 matplotlib 图例指南中有一章是关于 custom legend handlers 的。您可以根据自己的需要进行调整,例如像这样:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.legend_handler import HandlerBase


class AnyObjectHandler(HandlerBase):
    def create_artists(self, legend, orig_handle,
                       x0, y0, width, height, fontsize, trans):
        l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height], 
                                                linestyle='--', color='k')
        l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height], color='r')
        return [l1, l2]


x = np.linspace(0, 3)
fig, axL = plt.subplots(figsize=(4,3))
axR = axL.twinx()

axL.plot(x, np.sin(x), color='k', linestyle='--')
axR.plot(x, 100*np.cos(x), color='r')

axL.set_ylabel('sin(x)', color='k')
axR.set_ylabel('100 cos(x)', color='r')
axR.tick_params('y', colors='r')

plt.legend([object], ['label'],
           handler_map={object: AnyObjectHandler()})

plt.show()

为了有多个这样的条目,可以提供一些参数元组,Handler 然后用来绘制图例。

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.legend_handler import HandlerBase


class AnyObjectHandler(HandlerBase):
    def create_artists(self, legend, orig_handle,
                       x0, y0, width, height, fontsize, trans):
        l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height],
                           linestyle=orig_handle[1], color='k')
        l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height], 
                           color=orig_handle[0])
        return [l1, l2]


x = np.linspace(0, 3)
fig, axL = plt.subplots(figsize=(4,3))
axR = axL.twinx()

axL.plot(x, np.sin(x), color='k', linestyle='--')
axR.plot(x, 100*np.cos(x), color='r')

axL.plot(x, .3*np.sin(x), color='k', linestyle=':')
axR.plot(x, 20*np.cos(x), color='limegreen')

axL.set_ylabel('sin(x)', color='k')
axR.set_ylabel('100 cos(x)', color='r')
axR.tick_params('y', colors='r')

plt.legend([("r","--"), ("limegreen",":")], ['label', "label2"],
           handler_map={tuple: AnyObjectHandler()})

plt.show()

【讨论】:

  • 很好,谢谢。应该注意的是,为了拥有几个不同的“组”行,必须创建不同的AnyObject 类,这些类可以用作键。
  • 我更新了解决方案,适用于您希望拥有多个此类条目的情况。
【解决方案2】:

这是我在玩过之后得出的一个不充分的解决方案。我发布它以防它帮助其他人,但更愿意正确地做。它使用两列,第一列的标签作为'/',或者同样可以使用“and”。

import matplotlib.pyplot as plt

x = np.linspace(0, 3)
fig, axL = plt.subplots()
axR = axL.twinx()

axL.plot(x, np.sin(x), color='k', label='/')
axR.plot(x, 100*np.cos(x), color='r', label='label')

axL.set_ylabel('sin(x)', color='k')
axR.set_ylabel('100 cos(x)', color='r')
axR.tick_params('y', colors='r')

handlesL, labelsL = axL.get_legend_handles_labels()
handlesR, labelsR = axR.get_legend_handles_labels()
handles = handlesL + handlesR
labels = labelsL + labelsR
axR.legend(handles, labels, loc='lower center', ncol=2, fontsize=16,
           handletextpad=0.4, columnspacing=0.4)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多