I feel like this should be in the cookbook somewhere, but I cannot find it. I’m trying to display a scatter plot and data frame, and filter the displayed data frame by selecting elements of the scatter plot.
So far I have this:
import holoviews as hv
import panel as pn
import pandas as pd
import numpy as np
hv.extension('bokeh')
# Generate random data
num_points = 100
np.random.seed(42) # For reproducibility
x = np.random.rand(num_points)
y = np.random.rand(num_points)
colors = np.random.rand(num_points)
# Create a DataFrame
df = pd.DataFrame({
'x': x,
'y': y,
'color': colors
})
# Create a Holoviews Points element for the scatter plot
scatter = hv.Points(df, kdims=['x', 'y'], vdims=['color']).opts(color="color", tools=["tap"])
# Create a Panel DataFrame widget to display the DataFrame
df_table = pn.widgets.DataFrame(df, name='DataFrame')
# Create a layout with scatter plot and data frame side by side
layout = pn.Row(scatter, df_table)
layout.servable()
This only gets so far as putting the scatter plot side by side. I’d like to add a stream (I think Tap) and use it (with pn.bind? Or is that wrong?). The table should then only display selected values.
import numpy as np
import pandas as pd
import panel as pn
hv.extension("bokeh")
# Generate random data
num_points = 100
np.random.seed(42)
x = np.random.rand(num_points)
y = np.random.rand(num_points)
colors = np.random.rand(num_points)
# Create a DataFrame
df = pd.DataFrame({"x": x, "y": y, "color": colors})
# Create a Holoviews Points element for the scatter plot
scatter = hv.Points(df, kdims=["x", "y"], vdims=["color"]).opts(tools=["tap"], size=10)
# Create a Tap stream to capture clicks
tap_stream = hv.streams.Tap(source=scatter)
# Create a Panel DataFrame widget to display the DataFrame
df_table = pn.widgets.DataFrame(df, name="DataFrame")
# Define a function to update the DataFrame based on the clicked point
def update_df(click_event_x, click_event_y):
# Find the closest point in the DataFrame to the clicked coordinates
distances = (
(df["x"] - click_event_x.new) ** 2 + (df["y"] - click_event_y.new) ** 2
) ** 0.5
closest_index = distances.idxmin()
# Create a DataFrame for the selected point
selected_df = df.loc[[closest_index]]
return selected_df
# Define a function to update the DataFrame widget
def update_table(x, y):
df_table.value = update_df(x, y)
# Bind the update function to the Tap stream
tap_stream.param.watch(update_table, ["x", "y"])
# Display the scatter plot and data frame
layout = pn.Row(scatter, df_table)
layout.servable()