'How to group legend handles in matplotlib?

I was wondering how I can produce such fancy legends in matplotlib. In particular, I'd like to know how to group, for example, both solid and dashed blue lines which correspond to $\tau=10$ side by side, or all dashed (or solid) lines together as in the lower part of the legend.

enter image description here

The image is taken from this arxiv paper.



Solution 1:[1]

Thanks to the comments on my original post, I could come up with a script that does the grouping, albeit as mentioned, not as straightforwardly as I thought. The script is essentially an adapted version of the other answer on 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()})

while leads to

enter image description here


Take-home messages

  1. The legend docs specifies a more natural way to group the entries using HandlerTuple (example here). But since mpl places the markers horizontally, such an approach is orthogonal to what I wished :). If it doesn't bother you, go for that option first.

  2. As far as I understood, custom legends are designed such that they don't exchange any information with the data you want to plots. For example, in my case, I cannot tell the AnyObjectHandler how many lines are going to be grouped, what are their linestyle, etc. It is a good decision for generality, with the expense of a (minimal) harm to the code refactoring.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 arash