Not sure where exactly you get stuck.
There is some useful documentation in Holoviews:
https://holoviews.org/user_guide/Large_Data.html
With the large set of datapoints (10m) you likely need to use the datashader or something similar to get any meaningful graphical output.
Anyway, I have only played with it once to integrate into my app to provide support for filtering down a Tabulator based on selections on several plots. Below a slightly streamlined version of an adhoc custom graphical filter I use in my app. Requires holoviews,
For lasso-select to work, you’ll also need to install shapely and datashader.
import pandas as pd
import numpy as np
import param
import panel as pn
import holoviews as hv
from holoviews.operation.datashader import rasterize
pn.extension()
class FilterUI(param.Parameterized):
# user input parameter
df = param.DataFrame(doc='Complete Dataset')
# output stuff
df_selected = param.DataFrame(doc='Selected Dataset')
ui = param.Parameter(default=None,doc='UI Pane')
ui_output = param.Parameter(default=None, doc='UI Output Pane')
link_selections_instance = param.Parameter(default=None, doc='Link Selection Instance')
def view(self):
# we always recreate the UI from scratch
if self.ui is None:
self.ui = pn.Column()
else:
self.ui.clear()
# reset what's selected
self.df_selected = self.df
# create our plot
plot_opts = {'tools': ['hover', 'box_select','lasso_select'],
'active_tools': ['box_select'],
'width': 600, 'height': 600}
plot = hv.Points(self.df, ['x', 'y']).opts(**plot_opts)
if len(self.df) > 1000:
plot = rasterize(plot).opts(colorbar=True, **plot_opts)
# create our output placeholder container.
self.ui_output = pn.Column('Nothing selected yet')
# create a selection instance if not already done.
# Avoid having
# the @param.depends trigger at this stage. There are other ways to do this,
# like using low level API self.link_selections_instance.param.watch() ...)
if self.link_selections_instance is None:
with param.parameterized.discard_events(self):
self.link_selections_instance = hv.link_selections.instance()
# create a few widgets to control some of its settings on the link_selections_instance
self.link_selections_instance = hv.link_selections.instance()
ls_widgets = pn.Param(
self.link_selections_instance, parameters=[
# 'cross_filter_mode', .... only useful if multiple plots
'selection_mode'], name='Linking Options')
# add the plot to a layout
layout = hv.Layout([plot])
# Add widgets and the Plot-Layout to the UI Pane
self.ui.extend([
pn.Row(ls_widgets, self.link_selections_instance(layout)),
self.ui_output])
return self.ui
@param.depends('link_selections_instance.selection_expr', watch=True)
def cb_link_selections(self, event=None):
print(f'cb_link_selections()')
# filter the DF with the selection criteria
filter_expr = self.link_selections_instance.selection_expr
self.df_selected = self.link_selections_instance.filter(self.df, filter_expr)
# prepare and push some output stats/info
self.ui_output.clear()
self.ui_output.extend(
[pn.pane.DataFrame(self.df_selected, max_rows=10),
pn.pane.Str(f'Shape: {self.df_selected.shape}'),
pn.pane.Str(f'Selection Expr: {filter_expr}')
])
#self.ui_html_output.object = self.df_selected.to_html(max_rows=10)
# now run a test
entries = 100000
df = pd.DataFrame({'x': np.random.normal(loc=0.5, scale=0.2, size=entries),
'y': np.random.normal(loc=0.2, scale=0.4, size=entries),
'users': np.random.choice(list('abcdefg'), entries)})
test = FilterUI(df=df)
test.view().show()
regards
Johann