如何在 matplotlib 中对图例句柄进行分组?

How to group legend handles in matplotlib?

我想知道如何在 matplotlib 中制作出如此奇特的图例。特别是,我想知道如何分组,例如,并排对应于 $\tau=10$ 的蓝色实线和虚线,或者所有虚线(或实线)在一起,如传说。

图片来自this arxiv paper.

多亏了对我原来的 post 的评论,我可以想出一个进行分组的脚本,尽管如前所述,但并不像我想的那么直接。该脚本本质上是 SO 上其他答案的改编版本。

import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib import patches as mpatches
from matplotlib.collections import PatchCollection

class AnyObject(object):
    pass

class AnyObjectHandler(object):
    def legend_artist(self, legend, orig_handle, fontsize, handlebox):
        x0, y0 = handlebox.xdescent, handlebox.ydescent
        width, height = handlebox.width, handlebox.height
        
        codes = [Path.MOVETO, Path.LINETO]
        
        # the following lines unfortunately may not be refactored
        verts1 = [(x0, y0+0.25*height),(x0 + width, y0+0.25*height)]
        verts2 = [(x0, y0+0.75*height),(x0 + width, y0+0.75*height)]
        
        path1 = Path(verts1,codes)
        path2 = Path(verts2,codes)
        
        patch1 = mpatches.PathPatch(path1)
        patch2 = mpatches.PathPatch(path2,ls='--',)
        patch = PatchCollection([patch1,patch2],match_original=True)

        handlebox.add_artist(patch)
        return patch
    

fig, ax = plt.subplots()
ax.legend([AnyObject()], ['My grouped handlers'],
          handler_map={AnyObject: AnyObjectHandler()})

同时导致


Take-home 条消息

  1. 图例文档指定了一种使用 HandlerTuple(示例 here)对条目进行分组的更自然的方式。但是由于 mpl 水平放置标记,这种方法与我希望的正交:)。如果您不介意,请先选择该选项。

  2. 据我了解,自定义图例的设计使其不会与您要绘制的数据交换任何信息。例如,在我的例子中,我无法告诉 AnyObjectHandler 有多少行将被分组,它们是什么 linestyle,等等。这是一个很好的通用性决定,代价是 (最小)对代码重构的危害。