Search code examples
pythonlegendholoviewsholoviz

HoloViews - NdOverlay - Legend


I have a hv.NdOverlay plot of multiple holoviews.Curve objects. The Curve objects belong to a group (in this case either group 'A' or group 'B'). How do I set up the hv.NdOverlay plot to show a legend which shows the two groups?

Code example (holoviews version: 1.13.4):

import holoviews as hv
import numpy as np
from datetime import datetime
from holoviews import opts

hv.extension('bokeh')

x_start = [0, 100, 100, 0, 120, 200]
x_end = [220, 340, 280, 120, 290, 400]
date = [datetime(2020, 2, 10, 0, 0, 0), datetime(2018, 5, 22, 0, 0, 0),
        datetime(2012, 7, 8, 0, 0, 0), datetime(1999, 12, 28, 0, 0, 0), 
        datetime(2004, 3, 21, 0, 0, 0), datetime(1992, 11, 23, 0, 0, 0)]
group = ['A', 'A', 'B', 'A', 'A', 'B']

curves_dict = dict()
for key in range(len(x_start)):
    curves_dict[key] = hv.Curve(((x_start[key], x_end[key]),
                                 (np.datetime64(date[key]), np.datetime64(date[key]))),
                                'X', 'Year',
                                group=group[key])

curves_ndoverlay_all = hv.NdOverlay(curves_dict)
curves_ndoverlay_all.opts(
    opts.NdOverlay(show_grid=True, height=300, responsive=True, align='center'),
    opts.Curve('A', color='red'),
    opts.Curve('B', color='green'))

What I get is this: Line plo, 2 groups

What I would like to get is a legend with 2 entries, one displaying a red line followed by the letter 'A' and the other one displaying a green line followed by the letter 'B'. It looks like holoviews.NdOverlay is pulling the information for the legend from the keys of the curves_dict. But the keys must be unique. So it looks like this won't work for my intended plot. Is there a different way to set up the holoviews.NdOverlay so that I get the legend I described above?


Solution

  • Is this the result you are looking for?

    Legend with two items

    This is the code I was using to create it:

    import holoviews as hv
    import numpy as np
    from datetime import datetime
    from holoviews import opts
    
    hv.extension('bokeh')
    
    x_start = [0, 100, 100, 0, 120, 200]
    x_end = [220, 340, 280, 120, 290, 400]
    date = [datetime(2020, 2, 10, 0, 0, 0), datetime(2018, 5, 22, 0, 0, 0),
            datetime(2012, 7, 8, 0, 0, 0), datetime(1999, 12, 28, 0, 0, 0), 
            datetime(2004, 3, 21, 0, 0, 0), datetime(1992, 11, 23, 0, 0, 0)]
    group = ['A', 'A', 'B', 'A', 'A', 'B']
    
    def fig(key):
        return hv.Curve(((x_start[key], x_end[key]), (np.datetime64(date[key]), np.datetime64(date[key]))),
            'X', 'Year',
            group=group[key],
            label=group[key]
            )
    
    for i in range(len(group)):
        if i == 0:
            f = fig(i)
        else:
            f *= fig(i)
            
    f.opts(opts.Curve(show_grid=True, height=300, responsive=True, align='center'),    
           opts.Curve('A', color='red'),
           opts.Curve('B', color='green'))
    
    

    There are two things different to your approach. I made use of the label parameter of the hv.Curve() and I don't use NdOverlay anymore.