Dynamically fetch other column value from selection1D stream

Hi there,
I got a problem that I hope might have a simple solution.
I got a points plot and a box select for this. The box_select feed a BoundsXY stream that is used in a dynamic map to populate a holoviews table. The table is reporting by an identfier and I want to use a selection1D stream to fetch the selected row(s). When selections are done in the table I want to catch the value of the ‘id’ column. rather than the index as integer location for the selected row. It seems holoviews table does not let there be a custom index in the table so I guess the question is if something similar to a dynamic map can be applied so that a function can dynamically map the index to the ‘col’ identifier I am after. I am sorry the code is a bit constructed, but hope someone has got a good idea. This seems like a silly question, but is really an obstackle for me currently.

import pandas as pd
import numpy as np
import hvplot.pandas
hv.extension('bokeh')
from holoviews.streams import Stream, Selection1D

size = 400
arr = np.c_[np.random.normal(0.0, 10, size), np.random.normal(400, 200, size)] # , colors]

colors = ['blue','red'] * 200
ids = ['A', 'B', 'C', 'D'] * 100
df = pd.DataFrame(arr, columns=['x','y'])
df['col'] = colors
df['id'] = ids
# df.head()

def points_plot():
    return df.hvplot.scatter(x='x', y='y', color='col' ).opts(height=300, width=300, invert_yaxis=True, 
                                                              tools=['box_select', 'lasso_select'], 
                                                              active_tools=['box_select'])
points = points_plot()
# points

box_stream = hv.streams.BoundsXY(bounds=(0,0,0,0), source=points)

def get_tabl(bounds):
    bounds_x = (bounds[0], bounds[2])
    bounds_y = (bounds[1], bounds[3])
    df2 = df.loc[(df['x'].between(bounds_x[0], bounds_x[1]))
                 & (df['y'].between(min(bounds_y), max(bounds_y)))].groupby(['id'])['x','y'].max().reset_index(drop=False) # ['col']
    return hv.Table(df2)
                       

dmap = hv.DynamicMap(get_tabl, streams=[box_stream])

stream_ids = hv.streams.Selection1D(index=[0], source=dmap)

points + dmap```

I think I’m following your question, but please clarify if I’m misunderstanding. My main question is what you want to do with these IDs after the fact. What you can do is something like this:

def get_ids(table, index):
    return hv.Dataset(table.iloc[index]['id'], 'id')

stream_ids = hv.streams.Selection1D(source=dmap)
id_dmap = dmap.apply(get_ids, streams=[stream_ids])
id_dmap[()].data

which will give you a dataframe of the current ids. To explain what is going on:

  1. We define the function get_ids which accepts the Table and Selection1D.index
  2. We use .apply to map this function to the Tables in the dmap and provide the Selection1D stream to this apply function.
  3. We can now evaluate the id_dmap with [()] to get the Dataset of the current IDs and access its .data attribute

Depending on what you want to do with these ids you could modify get_ids to return some other displayable element which you could add to your layout.

2 Likes

Thank you very much for your explanation. What I want to do with the id (I actually want to limit it to max one selected id if possible) is to use it as an argument into another function for making a layout of several sideways alingned line plot elements where x (and potential other variables are a function of y (where in my case y could be the positive value downwards measured depth value of drilled wellbores into the ground).

It was iteresting to see the notation " [()]" in your id_dmap[()].data.

My feeling is that everything ispossible when one just gets the grip onthe streams and how to fetch events Maybe also there is a need to make custom streams. But I wonder if not it would have been beneficial that hv.Table elements could have had their own user defined index that could have been accessed from Selection1D with a potential max_selected_items argument. Or that one might choose to retrieve the hole row for a Selection1D for a hv.Table. This might provide a more readable path into the id by a .iloc[index][‘id’] possibly, but maybe I am wrong there…

Anyway, thanks for your great efforts. The holoviz framework really makes a difference!