Points:
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn
import param
hv.extension("bokeh")
pn.extension("tabulator")
grid_size = 3
grid_bounds = (-0.5, grid_size - 0.5)
grid_x, grid_y = np.meshgrid(range(grid_size), range(grid_size))
grid_data = pd.DataFrame(
{
"x": grid_x.flatten(),
"y": grid_y.flatten(),
"value": np.random.uniform(size=grid_size * grid_size),
}
)
def selection1d_to_tabulator(index):
tabulator.selection = index
def tabulator_to_selection1d(selection):
selection_1d.event(index=selection)
def create_points(index):
return hv.Points(grid_data).opts(selected=index, size=25)
selection_1d = hv.streams.Selection1D()
tabulator = pn.widgets.Tabulator(grid_data, selectable=True, disabled=True)
dmap_heatmap = hv.DynamicMap(create_points, streams=[selection_1d]).opts(tools=["tap"])
pn.bind(selection1d_to_tabulator, selection_1d.param.index, watch=True)
pn.bind(tabulator_to_selection1d, tabulator.param.selection, watch=True)
pn.Column(tabulator, dmap_heatmap).show()
HeatMap
Since heatmap doesn’t have selected
, we have be semi-hacky by introducing an underlay and overlay (maybe someone can find a better way). We also don’t want to simply use iloc
because it makes the heatmap rectangles stretch, so we have to set non-selected values to NaN to keep its shape. Watch out for the transposed indices!
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn
hv.extension("bokeh")
pn.extension("tabulator")
grid_size = 3
grid_bounds = (-0.5, grid_size - 0.5)
grid_x, grid_y = np.meshgrid(range(grid_size), range(grid_size))
grid_data = pd.DataFrame(
{
"x": grid_x.flatten(),
"y": grid_y.flatten(),
"value": np.random.uniform(size=grid_size * grid_size),
}
)
def selection1d_to_tabulator(index):
tabulator.selection = index
def tabulator_to_selection1d(selection):
selection_1d.event(index=selection)
def create_points(index):
heatmap = hv.HeatMap(grid_data, kdims=["x", "y"], vdims=["value"]).opts(
xlim=grid_bounds,
ylim=grid_bounds,
colorbar=True,
clim=(0, 1),
clipping_colors={"NaN": "transparent"},
alpha=0.5
)
selected_data = grid_data.copy()
if len(index) > 0:
# HoloViews HeatMap index is transposed :(
n_x = len(grid_data["x"].unique())
n_y = len(grid_data["y"].unique())
selected_data.index = np.array(grid_data.index).reshape(n_x, n_y).transpose().ravel()
selected_data.loc[~selected_data.index.isin(index), 'value'] = pd.NA
return heatmap * heatmap.clone(selected_data).opts(alpha=1, tools=["tap"])
selection_1d = hv.streams.Selection1D()
tabulator = pn.widgets.Tabulator(grid_data, selectable=True, disabled=True)
dmap_heatmap = hv.DynamicMap(create_points, streams=[selection_1d])
pn.bind(selection1d_to_tabulator, selection_1d.param.index, watch=True)
pn.bind(tabulator_to_selection1d, tabulator.param.selection, watch=True)
pn.Column(tabulator, dmap_heatmap).show()
Ideally, tabulator.link(selection_1d, selection="index")
would work, but Support panel widget.link(stream, selection="index") · Issue #7801 · holoviz/panel · GitHub.
Thanks to all of these Discourse posts for helping me get this solution: