Updating elements in HoloViews object in param class too slow

I’d like to update the color of a selection of bars (ListSelection) in multiple HoloViews barplots (one per criteria). So far I’ve come up with this (code below gif):

  • each criterium bar plot is an hv.Overlay of the all bars and the selection of bars
  • changing the selection triggers a function that updates the selection of bars plot
  • a show_layout method returns an hv.Layout of all above overlays
  • a class attribute layout displays the panel (Layout and List param)

Changng a selection takes about 5 seconds, which is too slow to use interactively. The hv.Layout seems to be renewed with every new selection. Is there a faster way e.g. is there a way to simply change elements in the HoloViews object and have the updates take place?

import numpy as np
import panel as pn
import pandas as pd
import param
import holoviews as hv
pn.extension()

import hvplot.pandas

criteria =[f'criterium_{x}' for x in range(20)]
parameters =[f'parameter_{x}' for x in range(8)]
df = pd.DataFrame(columns=parameters,
                  index=criteria,
                  data=np.random.rand(len(criteria),
                                      len(parameters)))

class Update_HoloViews_callback(param.Parameterized):
    """Plots all layout with overlays of 2 barplots"""

    pars = param.ListSelector(default=parameters[:1],
                                    objects=parameters)
    
    def __init__(self,**params):
        for criterium in criteria:
            barplot_all = df.loc[criterium,:].hvplot.bar(legend=False,rot=90).opts(width=200,height=250)
            barplot_selection = barplot_all[self.pars] 
            barplot_combined = barplot_all*barplot_selection
            setattr(self,criterium+'_plot', barplot_combined)
            
        super().__init__(**params)
        self.layout = pn.Row(self.show_layout,
                             pn.Param(self))
    
    @pn.depends('pars',watch=True)
    def update_layout(self):
        for i,attr in enumerate(criteria):
            exec(f'self.{attr}_plot.Bars.{attr}.II=self.{attr}_plot.Bars.{attr}.I[self.pars]')
        

    def show_layout(self):
        barplot_layout = []
        for criterium in criteria:
            barplot_layout.append(getattr(self,f'{criterium}_plot'))
        return hv.Layout(barplot_layout)
    

p = Update_HoloViews_callback()

p.layout

Hi @mcav

I don’t know for sure. But I have a feeling that using HoloViews layouts, holomap etc. only would speed up things.

I would look to something like http://holoviews.org/user_guide/Building_Composite_Objects.html.

If you get something working. Please share. I would really like to learn how to do this too.

Thanks.

Thanks @Marc. I’m currently using hv.Layout for the subplots and hvplot.bar and hv.Overlay for my barplots.
Do you think holomaps make a difference? I’m not too familiar with them. :slight_smile:

The remaining issue is that I can’t seem to update the HoloViews elements AND update the display of these elements without having a method returning the full layout (which is the slow part).

In Updating matplotlib figure in Parameterized class, I did see @philippjfr suggest a trigger functionality on a matplotlib object.

self.mpl.param.trigger('object')

I didn’t get it working for the hv.Layout object, but even then I’m not sure if a similar approach on a HoloViews object would resolve the speed issue?
does anyone know of documentation / examples of trigger on HoloViews?

Some time since I’ve posted this, but there is significant speed improvement:
I’ve found using a hv.DynamicMap and a hook to update the selected bars is the way to go here.

import numpy as np
import panel as pn
import pandas as pd
import param
import holoviews as hv
pn.extension()

import hvplot.pandas

criteria =[f'criterium_{x}' for x in range(20)]
parameters =[f'parameter_{x}' for x in range(8)]
df = pd.DataFrame(columns=parameters,
                  index=criteria,
                  data=np.random.rand(len(criteria),
                                      len(parameters)))

class Update_HoloViews_callback(param.Parameterized):
    """Plots all layout with overlays of 2 barplots"""

    pars = param.ListSelector(default=parameters[:1],
                                    objects=parameters)
    
    def __init__(self,**params):
            
        super().__init__(**params)
        plot = hv.DynamicMap(self.show_layout)
        self.layout = pn.Row(plot,
                             pn.Param(self))
   
    def hook(self,plot,element):
        plot.handles['selected'].indices = [i for i,par in enumerate(parameters) if par in self.pars]
     
    @pn.depends('pars')
    def show_layout(self):
        bar_opts = dict(show_legend=False,xrotation=90,width=200,height=250,
                             axiswise=False, default_tools=[])
        barplot_list = []
        for attribute in criteria:
            barplot = df.loc[attribute,:].hvplot.bar(nonselection_alpha=1,selection_color='orange').opts(**bar_opts,hooks=[self.hook])
            barplot_list.append(barplot)
        mylayout = hv.Layout(barplot_list).cols(4)
        return mylayout
    

p = Update_HoloViews_callback()

p.layout

1 Like