散点图不使用 Python 面板中的小部件选择更新 Holoviz

Scatter Plot Not Updating With Widget Selection in Python Panel Holoviz

提问人:Max Power 提问时间:5/15/2023 更新时间:5/25/2023 访问量:231

问:

我想要一个具有动态绘图的 Python 面板应用程序,以及根据从侧面板中选择的菜单/按钮的不同绘图,所以我从这个很好的起点开始:https://discourse.holoviz.org/t/multi-page-app-documentation/3108/2

每个页面都有一个正弦和余弦图,该图根据小部件值进行更新,使用 .@pn.depends

然后,我更新了代码,使正弦图成为散点图,小部件将是一个选择下拉菜单,而不是一个滑块。但是现在,当我更新选择下拉小部件时,散点图没有更新。我做错了什么?显然,我对正在做什么或不做什么有些误解。任何帮助将不胜感激!@pn.depends()

完整代码 () 如下:app.py

import pandas as pd
import panel as pn
import holoviews as hv
import hvplot.pandas
import numpy as np
import time
from datetime import datetime

pn.extension()

template = pn.template.FastListTemplate(title='My Dashboard')

# load detailed data
durations = np.random.randint(0, 10, size=10)
activity_codes = ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3', 'C3']
activity_categories = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'C']

df = pd.DataFrame({'Duration': durations,
                   'ActivityCode': activity_codes,
                   'ActivityCategory': activity_categories})


# Page 1 Widget Controls
ac_categories = ['A', 'B', 'C']
ac_cat_radio_button = pn.widgets.Select(name='Activity Category', options=ac_categories)


# Page 1 Plotting Code
@pn.depends(ac_cat=ac_cat_radio_button)
def scatter_detail_by_ac(df, ac_cat):
    print(f"ac_cat is {ac_cat}")
    print(f"ac_cat.value is {ac_cat.value}")
    df_subset = df.loc[df.ActivityCategory==ac_cat.value]

    print(f"number of records in df_subset to scatter plot: {len(df_subset):,}")
    return df_subset.hvplot.scatter(x='ActivityCode', y='Duration')


freq2 = pn.widgets.FloatSlider(name="Frequency", start=0, end=10, value=2)
phase2 = pn.widgets.FloatSlider(name="Phase", start=0, end=np.pi)
@pn.depends(freq=freq2, phase=phase2)
def cosine(freq, phase):
    xs = np.linspace(0,np.pi)
    return hv.Curve((xs, np.cos(xs*freq+phase))).opts(
        responsive=True, min_height=400)

page = pn.Column(sizing_mode='stretch_width')

content1 = [
    pn.Row(ac_cat_radio_button), #grouping_vars_radio_button),
    scatter_detail_by_ac(df, ac_cat_radio_button),
]
content2 = [
    pn.Row(freq2, phase2),
    hv.DynamicMap(cosine),
]

link1 = pn.widgets.Button(name='Scatter')
link2 = pn.widgets.Button(name='Cosine')

template.sidebar.append(link1)
template.sidebar.append(link2)

template.main.append(page)

def load_content1(event):
    template.main[0].objects = content1


def load_content2(event):
    template.main[0].objects = content2

link1.on_click(load_content1)
link2.on_click(load_content2)

template.show()

我通过运行(从 shell)在本地提供此面板应用程序:

panel serve app.py --autoreload

另外,这是我正在使用的库版本,从我的(我进入 requirements.txt/lockfile):requirements.inpip-compile

panel==v1.0.0rc6
pandas==1.5.3
holoviews==1.16.0a2
hvplot
pandas-gbq>=0.19.1
python pandas hvplot holoviz holoviz-panel

评论


答:

2赞 MaximeL 5/25/2023 #1

您遇到的问题是,当您标记一个函数时,您需要将其按原样传递给 Panel,Panel将负责重新执行它并在装饰器中列出的小部件之一更新时重新呈现其输出。下面是代码的简化版本,修复了您遇到的问题:pn.dependspn.depends

import pandas as pd
import panel as pn
import hvplot.pandas
import numpy as np

pn.extension()

# load detailed data
durations = np.random.randint(0, 10, size=10)
activity_codes = ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3', 'C3']
activity_categories = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'C']

df = pd.DataFrame({'Duration': durations,
                   'ActivityCode': activity_codes,
                   'ActivityCategory': activity_categories})


# Page 1 Widget Controls
ac_categories = ['A', 'B', 'C']
ac_cat_radio_button = pn.widgets.Select(name='Activity Category', options=ac_categories)

from functools import partial

# Page 1 Plotting Code
@pn.depends(ac_cat=ac_cat_radio_button)
def scatter_detail_by_ac(df=df, ac_cat=None):
    df_subset = df.loc[df.ActivityCategory==ac_cat]
    print(f"number of records in df_subset to scatter plot: {len(df_subset):,}")
    return df_subset.hvplot.scatter(x='ActivityCode', y='Duration')


pn.Column(ac_cat_radio_button, scatter_detail_by_ac)

通常,现在建议面板用户使用,而不是在他们想要在其应用程序中添加交互性时使用。它的行为类似于 这使得它更容易接近,特别是如果您已经熟悉 .pn.bindpn.dependsfunctools.partialfunctools.partial

现在,当涉及到更具体的数据应用程序(例如您的应用程序)时,您还可以利用 API,您可以使用该 API 将管道值替换为面板小部件。我使用此 API 重新编写了您的酷应用程序:hvplot.interactive

import pandas as pd
import panel as pn
import hvplot.pandas
import numpy as np

pn.extension(sizing_mode='stretch_width')

# load detailed data
durations = np.random.randint(0, 10, size=10)
activity_codes = ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3', 'C3']
activity_categories = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'C']

df = pd.DataFrame({'Duration': durations,
                   'ActivityCode': activity_codes,
                   'ActivityCategory': activity_categories})


# App 1
w_ac_cat = pn.widgets.Select(name='Activity Category', options=['A', 'B', 'C'])
dfi = df.interactive()
dfi = dfi.loc[dfi.ActivityCategory == w_ac_cat]
app1 = dfi.hvplot.scatter(x='ActivityCode', y='Duration', responsive=True, min_height=400)

# App 2

def cosine(freq, phase):
    xs = np.linspace(0, np.pi)
    return pd.DataFrame(dict(y=np.cos(xs*freq+phase)), index=xs)

w_freq = pn.widgets.FloatSlider(name="Frequency", start=0, end=10, value=2)
w_phase = pn.widgets.FloatSlider(name="Phase", start=0, end=np.pi)
dfi_cosine = hvplot.bind(cosine, w_freq, w_phase).interactive()
app2 = pn.Column(
    pn.Row(*dfi_cosine.widgets()),
    dfi_cosine.hvplot(responsive=True, min_height=400).output()
)

# Template

page = pn.Column(sizing_mode='stretch_width')
link1 = pn.widgets.Button(name='Scatter')
link2 = pn.widgets.Button(name='Cosine')
template = pn.template.FastListTemplate(
    title='My Dashboard', main=[page], sidebar=[link1, link2]
)

def load_content1(event):
    template.main[0][:] = [app1]

def load_content2(event):
    template.main[0][:] = [app2]

link1.on_click(load_content1)
link2.on_click(load_content2)

template.show()

enter image description here