How to update only the data of a plot (and not render the whole plot again)?

I’m looking for the general recipe to update only the data of plots inside a Panel app.


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.


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


df_pandas = pd.DataFrame(
        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"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)


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


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.