用于隐藏多个渲染器的 Bokeh 一个滑块

Bokeh one slider for hiding multiple renderers

我正在开发一个带有多个条形图的 Bokeh 应用程序。每个图表都有多个渲染器,一个用于每年的数据,所有图表都嵌入并 运行 在 Flask 上——一个图表的代码如下所示。

我想添加一个滑块,它将 show/hide 条对应于一年或几年的数据,但我只能在网上找到如何使滑块更改一个渲染器显示的数据,而不是完全摆脱它。我已经尝试了显示的两种方法 here。他们没有工作,因为:

有什么建议吗?提前致谢。


import datetime

import pandas as pd

from bokeh.layouts import widgetbox, column
from bokeh.models import Slider, HoverTool
from bokeh.plotting import ColumnDataSource, figure, show
from bokeh.embed import components
from bokeh.transform import dodge
from bokeh import palettes

BAR_GROUP_WIDTH = 0.6
MONTHS = [
    'January', 
    'Febuary', 
    'March', 
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
]

data = pd.DataFrame.from_dict(
    {'Month': ['January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
'2017': [11.6124, 7.11175, 9.366717, 11.071917, 4.849774, 10.0885, 14.81031, 15.486875, 14.877268, 14.025417, 11.203527, 11.09575],
'2018': [10.480874, 11.042208, 11.874903, 12.120542, 15.4275, 19.12525, 16.546917, 9.330083, 6.740833, 2.145, 5.919208, 15.946958],
'2019': [11.521308, 7.023, 9.282109, 10.518875, 12.664917, 9.953042, 13.494, 11.729083, 14.696792, 10.704042, 8.468698, 13.1605],
'2020': [11.162708, 7.276667, 15.194272, 16.654917, 19.552996, 20.480625, 14.800629, 7.722917, 19.260708, 18.052994, 17.488208, 19.442917],
'2021': [18.712944, 17.520792, 18.69113, 17.505542, 21.687413, 20.725637, 10.538542, 19.516708, 17.576167, 20.552875, 13.38375, 17.651667],
'2022': [17.027417, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
)

now = datetime.datetime.now()
years = [str(y) for y in range(2017, 2022)]
title = 'Test Plot'
source = ColumnDataSource(data=data)
p = figure(
    x_range=MONTHS, 
    y_range=(0, data[years].max().max() * 1.25), 
    height=350, width=900, 
    title=title.center(100)
)

renderers = []
for ix, y in enumerate(years):        
    # Here I generate one renderer for each group of twelve bars corresponding to all months in a year
    d = -BAR_GROUP_WIDTH / 2 + BAR_GROUP_WIDTH / (len(years) - 1) * ix
    r = p.vbar(x=dodge('Month', d, range=p.x_range), top=y, width=BAR_GROUP_WIDTH / len(years), 
        source=source, line_color="white", 
        color=palettes.Category10[len(years)][ix], 
        legend_label=y
    )
    tooltip = [
        ('YEAR', y),
        ('MONTH', '@Month'),
        ('TOTAL', f'@{y}')
    ]
    p.add_tools(HoverTool(tooltips=tooltip, renderers=[r]))
    renderers.append(r)

p.x_range.range_padding = 0.1
p.xgrid.grid_line_color = None
p.legend.location = "top_left"
p.legend.orientation = "horizontal"

# How do I make this slider do what I want it to do?
slider = Slider(start=2017, end=now.year, value=now.year, step=1, title="Range")

layout = column(p, widgetbox(slider))
show(layout)

# # My application is running on Flask and I'm embedding Bokeh components, so I actually use components rather than show.
# script, div = components(layout)
# return (script, div)

如果这就是您要找的

您可以使用每个渲染器都有一个属性 visible。您可以将其设置为 TrueFalse

您必须导入 CustomJS 然后编写一些 JavaScript 代码并通过回调将其连接到滑块。

callback = CustomJS(args=dict(renderers=p.renderers, year=2017), code="""
    const f = cb_obj.value;
    if(cb_obj.value == 2022){
        for(let r of renderers)
            r.visible = true
    }
    else{
        for(let r of renderers)
            r.visible = false
        renderers[f-year].visible = true
    }
""")
slider = Slider(start=2017, end=now.year, value=now.year, step=1, title="Range")
slider.js_on_change('value', callback)

在此示例的实现中,如果您 select 2022 年,则所有渲染器都设置为可见,否则只有 selected 年份的渲染器可见,其他所有渲染器都不可见。