Hello holoviews community!
I have a model with multiple plots (central map with points, and some additional bar charts) where the main map dataset is driven by a hv.DynamicMap (returning a hv.Table) based on several picklists, and then the bar charts are generated using hv_table.apply() with a selectionstream from the map.
It works beautifully UNLESS I lasso a set of points and then change the picklists for the main map; in which case the Selection1D stream based on the lasso tool will hit index bound issues (e.g. I select 200 points on the map with filter1=“A”, but changing filter1–>“B” results in an hv_table with only 150 rows; so now my bar charts which have index slicing “cached” from filter1=“A” + Selection1D stream crash because the indices are misaligned).
I can think of two rough pseudo-code approaches to attempt next:
- if I could clear the selection index programmatically when any of the pn.widget.Select objects that hv_table depends on change, that would likely solve the issues, but:
- it is not clear how I’d clear the selection index (Can I change which points are selected programmatically? - #2 by sbi_vm)
- how could I ensure that the theoretical “clear_index()” function triggers before the dynamic map updates trigger for each pn.widget.Select event?
- Instead of filtering my main geodataframe with a DynamicMap, maybe I could just use the dynamicmap to set the alpha=0.0 for the “out of scope” points? so that the index structure is consistent between the permutations of pd.widget values, but just the visualization changes? …but then a bunch of complexity gets pushed to all the related legends/labels/color maps I think…
Am I missing something obvious here? Anyone else have a suggestion for handling the overlap of selection streams and widgets–>DynamicMap ?
Here is a toy example: making a selection on dataset “A” then switching to dataset “B” causes issues:
import numpy as np
import holoviews as hv
from holoviews import dim
from holoviews import streams
import panel as pn
import pandas as pd
import random
hv.extension('bokeh')
data_sets = {}
cat_grp = ["apple", "banana", "pear"]
data_sets["A"] = pd.DataFrame(data=[(np.random.random(),np.random.random(),random.choice(cat_grp)) for i in range(2000)],columns=['x','y','fruit'])
data_sets["B"] = pd.DataFrame(data=[(np.random.random(),np.random.random(),random.choice(cat_grp)) for i in range(20)],columns=['x','y','fruit'])
dataset_selector = pn.widgets.Select(options=list(data_sets.keys()),
name="Dataset",
value="A",
width=250)
@pn.depends(ds = dataset_selector.param.value)
def set_selector(ds):
return hv.Table(data_sets[ds],kdims=['x','y'],vdims=['fruit'])
hv_table = hv.DynamicMap(set_selector)
points = hv_table.apply(hv.Points).opts(tools=['lasso_select'])
sel = streams.Selection1D(source=points)
def clear_sel(event):
print(f"old index was: {sel.index}")
sel.event(index=[])
print(f"new index is: {sel.index}")
def set_sel(event):
print(f"old index was: {sel.index}")
sel.event(index=[1,2,3])
print(f"new index is: {sel.index}")
def summary_table_sel(table,index):
if len(index)>0:
selected_df = table.data.iloc[index]
else:
selected_df = table.data
agg_table = selected_df.groupby('fruit').agg({'x':'count'}).reset_index()
agg_table.rename(columns={'x':'# of records'},inplace=True)
return hv.Table(agg_table,kdims=['fruit'],vdims=['# of records'])
summary_table = hv_table.apply(summary_table_sel,streams=[sel])
plt = points.opts(color='k', marker='x', size=10)
clr_btn = pn.widgets.Button(name='Clear Selection', button_type='primary')
clr_btn.on_click(callback = lambda event: clear_sel(event=event))
set_btn = pn.widgets.Button(name='Set Selection', button_type='primary')
set_btn.on_click(callback = lambda event: set_sel(event=event))
layout = pn.Row(plt,pn.Column(dataset_selector,clr_btn,set_btn),summary_table)
layout
It looks like I can manually click “Clear Selection” before switching A–>B and it prevents the crash, but the plot doesn’t update to show that the selection was cleared. (also I’d have to figure out how to trigger the clear_sel() function as the first event trigger off the dataset_selector change…)