Interaction between panel and holoviz - how to update plotted data

HI, I am working on a little widget with holoviews and panel - it consists of reading a pandas.dataFrame and display a curve for each column. The interaction I need is to be able to add/remove columns from the plot. In my real use case there are too many columns so I can’t use the legend interaction already provided by bokeh+holoviews.

I made a little example that ‘’’ kind of works ‘’’ but I am probably doing it wrong /…
The code is:

import holoviews as hv
import numpy as np
import pandas as pd
import colorcet as cc
import panel as pn

pn.extension()
hv.extension("bokeh")

# generate some data
def get_data():
    data = {
        "1998": np.random.rand(365),
        "1999": np.random.rand(365),
        "2000": np.random.rand(365),
        "2002": np.random.rand(365),
        "2003": np.random.rand(365),
    }
    df = pd.DataFrame(data, index=range(0, 365))
    return df

# utility to help me placing the month label around the 2nd week of each month

def split_list(a, n):
    k, m = divmod(len(a), n)
    return list(
        list(a[i * k + min(i, m) : (i + 1) * k + min(i + 1, m)]) for i in range(n)
    )


def get_ticks(df, pos):
    splitter = split_list(df.index, 12)
    months = [
        "Jan",
        "Feb",
        "Mar",
        "Apr",
        "May",
        "Jun",
        "Jul",
        "Aug",
        "Sep",
        "Oct",
        "Nov",
        "Dec",
    ]
    xticks_map = [i for i in zip([splitter[i][pos] for i in range(0, 12)], months)]
    return xticks_map

# plotting method

def get_mplot(df, cols=None):
    if cols:
        df = df[cols]
    if len(df.columns) == 0:
        print("No coumns selected")
        return None
    grid_style = {
        "grid_line_color": "black",
        "grid_line_width": 1.1,
        "minor_ygrid_line_color": "lightgray",
        "minor_xgrid_line_color": "lightgray",
        "xgrid_line_dash": [4, 4],
    }
    colors = cc.glasbey_light[: len(list(df.columns))]
    xticks_map = get_ticks(df, 15)
    multi_curve = [
        hv.Curve((df.index, df[v]), label=str(v)).opts(
            xticks=xticks_map,
            xrotation=45,
            width=900,
            height=400,
            line_color=colors[i],
            gridstyle=grid_style,
            show_grid=True,
        )
        for i, v in enumerate(df)
    ]
    mplot = hv.Overlay(multi_curve)
    return mplot


# get the data
df = get_data()

# create a multi-choice widget

years = pn.widgets.MultiChoice(
    name="Years", options=list(df.columns), margin=(0, 20, 0, 0)
)

# bind plot and multi-choice

@pn.depends(years)
def get_plot(years):
    df = get_data()
    if years:
        df = df[years]
    mplot = get_mplot(df, years)
    return mplot


pn.Column("Plot!", get_plot, pn.Row(years), width_policy="max").servable()

For convenience, I stored the code online as notebook on a gist:

notebook

My issue is with the interaction between holoviz and panel at cell #7 when I define the @pn.depends method - the only way I got it to work is to “reloading” the data at each interaction … (cell_out: [#21], [#3], in df = get_data() ) which obviously slows down the whole app if the data starts to increase.
Do you have any hints on how to avoid reload the dataset?

So, I found the problem. which is not related to the code itself but a weird behavior of panel+holoviz in a jupyter notebook environment.

Back to my code, this following simply work:

@pn.depends(years)
def get_plot(years):
    if years:
        df1 = df[years]
        mplot = get_mplot(df1, years)
    else:
        mplot = get_mplot(df)
    return mplot

with no need of generating the data in the get_plot method.

The problem I am facing is quite odd and hard to describe. see:

screen-record.gif

Essentially, if the above method throws an error and I fix it in the code, its execution in the panel widget callback will always pick up the version of the method that generates the first exception, ignoring any further change to the method itself.

The issue is not confined to a bug in my environment, as I was able to replicate it on a different Jupyter notebook server.