Can I change which points are selected programmatically?

Hi, a short question, with hopefully an easy answer:

Similar to the Bokeh question I would really like to set the active selection (~ box_select) in a Holoviews figure.
Is there a way to overwrite the selection?
I can think of a way by setting: streams.Selection1D.index = myindex, but this value cannot be modified.

ps: I failed to get the bokeh solution to work and I cant help but think there could be an better way in Holoview.

Also stuck on this; it seems like using “.event” on a Selection1D object does clear (or set) the index parameter on that stream, but the change does not get reflected to the plot. Here’s a quick test example I adapted from the scatter demo:

import numpy as np
import holoviews as hv
from holoviews import dim
from holoviews import streams
import panel as pn

hv.extension('bokeh')
np.random.seed(42)
coords = [(i, np.random.random()) for i in range(20)]
scatter = hv.Scatter(coords).opts(tools=['lasso_select'])

sel = streams.Selection1D(source=scatter)

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}")

plt = scatter.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(clr_btn,set_btn))
layout

Maybe there’s some attribute on the upstream holoviews object (scatter in this case?) that needs to be targeted instead of the stream? Anyone have ideas here?

1 Like

With the addition of a DynamicMap, I was able to get your code working. I also added a reset handler so that it works as expected after making a box selection on the plot.

import numpy as np
import holoviews as hv
from holoviews import dim
from holoviews import streams
import panel as pn
import pandas as pd

hv.extension('bokeh')
np.random.seed(42)
coords = [(i, np.random.random()) for i in range(20)]
scatter = hv.Scatter(coords).opts(tools=['box_select'], active_tools=['box_select'])

sel = streams.Selection1D(source=scatter)

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 select_points(index, resetting):
    # print(f"select_points index: {index}")
    selected = scatter.iloc[:]
    if index:        
        selected = scatter.iloc[index]
    else:
        selected = scatter.opts(selected=[i for i in range(20)])
    return  selected

plt = hv.DynamicMap(select_points, streams=[sel, streams.PlotReset()]).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(clr_btn,set_btn))
layout
3 Likes

And it looks like

selections

1 Like

I made a slight modification to do either filtering or brushing from the selection:

import numpy as np
import holoviews as hv
from holoviews import dim
from holoviews import streams
import panel as pn
import pandas as pd

hv.extension('bokeh')
np.random.seed(42)
coords = [(i, np.random.random()) for i in range(20)]
scatter = hv.Scatter(coords).opts(tools=['box_select'], active_tools=['box_select'])

sel = streams.Selection1D(source=scatter)

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 select_points(index, resetting):
    # print(f"select_points index: {index}")
    selected = scatter.iloc[:]
    if index:        
        # selected = scatter.iloc[index]  # filter unselected
        selected = scatter.opts(selected=[i for i in index])  # brush selected
    else:
        selected = scatter.opts(selected=[i for i in range(20)])
    return  selected

plt = hv.DynamicMap(select_points, streams=[sel, streams.PlotReset()]).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(clr_btn,set_btn))
layout
4 Likes

Your solution is much better @marcdhansenesi. Thanks for sharing.

selection

2 Likes

Thank you, @Marc. I have a hard time leaving well-enough alone. :wink:

I’ve been meaning to ask you about your animated gifs. What tool do you use to make them? They’re very nice.

1 Like

I record .mp4 files using Snagit. I often

(I of course don’t use the Online tools for something private/ proprietary).

2 Likes

Thanks, @Marc. That’s just what I was looking for.