如何使用 Panel 更新现有图?

How to update existing plot with Panel?

我有一个与 Bokeh 配合使用的仪表板应用程序。我正在尝试将其更改为使用 PanelGeoviews。我正在使用面板回调 API,因为这看起来最像我现有的 Bokeh 代码。我是 运行 Panel 服务器的常规 Python 脚本。

当我的回调为小部件选择创建新图时,Panel 会显示一个附加图而不是更新现有图。使用“servable”会在现有浏览器中显示额外的绘图 window,使用“show”会显示额外的 window。如何更新现有地块?

这是一些测试代码。 (完整的应用程序显示带有地理数据的等值线图,并且有更多带有读取不同数据的代码的小部件,但这段代码说明了问题。)

import census_read_data as crd
import census_read_geopandas as crg
import pandas as pd
import geopandas as gpd
import geoviews as gv
from bokeh.plotting import show
from bokeh.models import PrintfTickFormatter
import panel as pn

import hvplot.pandas

# Get Census Merged Ward and Local Authority Data
# Replaced by test DataFrame
geography = pd.DataFrame(data=[
    ['E36007378', 'Chiswick Riverside', 'E09000018', 'Hounslow'],
    ['E36007379', 'Cranford', 'E09000018', 'Hounslow'],
    ['E36007202', 'Ealing Broadway', 'E09000009', 'Ealing'],
    ['E36007203', 'Ealing Common', 'E09000009', 'Ealing'],
    ['E36007204', 'East Acton', 'E09000009', 'Ealing'],
    ['E09000018', 'Hounslow', 'E09000018', 'Hounslow'],
    ['E09000009', 'Ealing', 'E09000009', 'Ealing']
], columns=["GeographyCode", "Name", "LAD11CD", "LAD11NM"])

# Get London Ward GeoPandas DataFrame
# Replaced by test DataFrame
london_wards_data_gdf = pd.DataFrame(data=[
    ['E36007378', 'E09000018', 378],
    ['E36007379', 'E09000018', 379],
    ['E36007202', 'E09000009', 202],
    ['E36007203', 'E09000009', 203],
    ['E36007204', 'E09000009', 204]
], columns=["cmwd11cd", "lad11cd", "data"])

# Get LAD GeoPandas DataFrame
# Replaced by test DataFrame
london_lads_data_gdf = pd.DataFrame(data=[
    ['E09000018', 757],
    ['E09000009', 609]
], columns=["lad11cd", "data"])

locationcol = "GeographyCode"
namecol = "Name"
datacol = 'data'

# Panel
pn.extension('bokeh')
gv.extension('bokeh')

lad_max_value = london_lads_data_gdf[datacol].max()
ward_max_value = london_wards_data_gdf[datacol].max()
title = datacol + " by Local Authority"

local_authorities = geography['LAD11CD'].unique()
granularities = ['Local Authorities', 'Wards']

# Create Widgets
granularity_widget = pn.widgets.RadioButtonGroup(options=granularities)
local_authority_widget = pn.widgets.Select(name='Wards for Local Authority',
                                           options=['All'] +
                                           [geography[geography[locationcol] == lad][namecol].iat[0]
                                            for lad in local_authorities],
                                           value='All')
widgets = pn.Column(granularity_widget, local_authority_widget)
layout = widgets


def update_graph(event):
    # Callback recreates map when granularity or local_authority are changed
    global layout
    granularity = granularity_widget.value
    local_authority_name = local_authority_widget.value
    print(f'granularity={granularity}')

    if granularity == 'Local Authorities':
        gdf = london_lads_data_gdf
        max_value = lad_max_value
        title = datacol + " by Local Authority"
    else:
        max_value = ward_max_value
        if local_authority_name == 'All':
            gdf = london_wards_data_gdf
            title = datacol + " by Ward"
        else:
            local_authority_id = geography[geography['Name'] ==
                                           local_authority_name].iloc[0]['GeographyCode']
            gdf = london_wards_data_gdf[london_wards_data_gdf['lad11cd'].str.match(
                local_authority_id)]
            title = datacol + " by Ward for " + local_authority_name

    # Replace gv.Polygons with hvplot.bar for test purposes
    map = gdf.hvplot.bar(y=datacol, height=500)
    layout = pn.Column(widgets, map)

    # With servable, a new plot is added to the browser window each time the widgets are changed
    # layout.servable()

    # With servable, a new browser window is shown each time the widgets are changed
    layout.show()


granularity_widget.param.watch(update_graph, 'value')
local_authority_widget.param.watch(update_graph, 'value')
update_graph(None)

# panel serve panel_test_script.py --show

我最终使用参数而不是回调实现了我的解决方案,效果很好。但是,我最终看到了 ,它向我展示了我最初问题的解决方案。

回调不应 show() 新布局(使用新地图),而应仅更新现有布局,用新地图替换现有地图。当我写这篇文章时,它似乎很明显!

此代码片段显示了解决方案:

...
widgets = pn.Column(granularity_widget, local_authority_widget)
empty_map = pn.pane.Markdown('### Map placeholder...')
layout = pn.Column(widgets, empty_map)

def update_graph(event):
    ...
    # Replace gv.Polygons with hvplot.bar for test purposes
    map = gdf.hvplot.bar(y=datacol, height=500)

    # Update existing layout with new map
    layout[1] = map

granularity_widget.param.watch(update_graph, 'value')
local_authority_widget.param.watch(update_graph, 'value')

# Invoke callback to show map for initial widget values
update_graph(None)
layout.show()