I’m looking for the general recipe to update only the data of plots inside a Panel app.
Background
I have read from Panel: Add reactivity to components that it is possible to create reactive functions which return data and then to just update the data inside a plot:
This approach is preferred over rendering reactive functions directly because it is more efficient and updates only the specific Parameters that are being changed.
I have also read from Panel: HoloViews widgets [emphasize mine]:
HoloViews natively renders plots with widgets if a HoloMap or DynamicMap declares any key dimensions. Unlike Panel’s
interact
functionality, this approach efficiently updates just the data inside a plot instead of replacing it entirely.
In summary, there are ways to update the rendered plots by just updating the underlying data.
MWE
I will be using Dask dataframes, but in the MWE below it will be a pandas dataframe. The general structure of the app will be:
- Have widgets which filter the data. Here, only
select_highlow
(pn.widgets.Select) - Have calculation functions. Here, only
calculate(df, highlow)
which takes a dataframe + numerical/string values and returns a new, much smaller dataframe. The calculation functions need to be tested with unit tests. - Have graphs which show the filtered (small) data.
Example MWE code:
import pandas as pd
import panel as pn
import hvplot.pandas
pn.extension()
df_pandas = pd.DataFrame(
dict(
data=[1, 2, 3, 4, 1, 2, 3, 4],
ref=[1, 1, 1, 1, 10, 10, 10, 10],
category=["A", "A", "A", "A", "B", "B", "B", "B"],
highlow=["high", "high", "low", "low", "high", "high", "low", "low"],
),
)
def calculate(df, highlow):
mask = df["highlow"] == highlow
df_filtered = df[mask].copy()
df_filtered["err"] = abs(df_filtered["data"] - df_filtered["ref"])
return df_filtered.groupby("category")["err"].agg("mean")
def get_plot(df, highlow):
df_filtered = calculate(df, highlow)
return df_filtered.hvplot.bar(x="category", y="err")
select_highlow = pn.widgets.Select(name="High or low?", options=["high", "low"])
ibar = pn.bind(get_plot, df=df_pandas, highlow=select_highlow)
bar = pn.panel(ibar)
select_highlow.servable()
bar.servable()
This creates a working app. But isn’t this one rendering a reactive function (get_plot
) directly, because the bound function get_plot
returns something that is to be rendered; a hvplot.bar?
Question
How to edit this one so that it would only update the data? And how to do that in general*?
* in general case where (1) The original data is a large dataframe (only used as data source) (2) The data shown in graphs is using a smaller dataframe, which is always created from the original large dataframe, filtered based on widget values and aggregated thereafter.