如何使用 Bokeh 中的下拉菜单在图形之间切换?

How to toggle between figures using a dropdown menu in Bokeh?

我有一个图,其中我已经添加了按钮来显示不同的图。如何添加另外几个图形,每个图形都有不同的绘图集,并使用下拉菜单在图形之间切换?我正在尝试尽可能地压缩代码,以免为每个图形重写相同的函数。最好的方法是什么?在下面的示例代码中,我没有为第二个图包含一个滑块和三个按钮,但我希​​望所有图都有它们

import numpy as np
import pandas as pd
import warnings
from bokeh.layouts import widgetbox
from bokeh.plotting import figure, show, output_file, output_notebook
from bokeh.palettes import Spectral11, colorblind, Inferno, BuGn, brewer
from bokeh.models import HoverTool, value, LabelSet, Legend, ColumnDataSource, LinearColorMapper, BasicTicker, PrintfTickFormatter, ColorBar
from bokeh.models.widgets import DateRangeSlider, CheckboxButtonGroup
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.layouts import column, row
from json import loads
import ast
import datetime as dt

warnings.filterwarnings('ignore')

TOOLS = 'save,pan,box_zoom,reset,wheel_zoom'
p = figure(title="data", plot_height=400, tools=TOOLS, plot_width=1300)

start_date = dt.datetime.strptime('2019 04 15', '%Y %m %d')
end_date = dt.datetime.strptime('2019 04 18', '%Y %m %d')
t = np.arange(0.0, 2.0, 0.01)
dates = np.arange(start_date, end_date, np.timedelta64(1, 'h'),
                  dtype='datetime64')

x = np.sin(3*np.pi*t)[:72]
y = np.cos(3*np.pi*t)[:72]
z = np.cos(6*np.pi*t)[:72]

for c in [x, y, z]:
    c[40:50] = np.nan

source = ColumnDataSource(data={'Date': dates, 'x': x, 'y': y, 'z': z})

p.xaxis.axis_label = 'Date'
p.yaxis.axis_label = 'Position (m)'


def add_plot(y, color):
    new_plot = p.line(x='Date', y=y, line_width=1, color=color, source=source)
    return new_plot


x = add_plot('x', 'red')
y = add_plot('y', 'green')
z = add_plot('z', 'blue')

checkbox = CheckboxButtonGroup(labels=['x', 'y', 'z'], active=[0, 1, 2])
checkbox.callback = CustomJS(args=dict(x=x, y=y, z=z), code="""
    //console.log(cb_obj.active);
    x.visible = false;
    y.visible = false;
    z.visible = false;
    for (i in cb_obj.active) {
        //console.log(cb_obj.active[i]);
        if (cb_obj.active[i] == 0) {
            x.visible = true;
        } else if (cb_obj.active[i] == 1) {
            y.visible = true;
        } else if (cb_obj.active[i] == 2) {
            z.visible = true;
        }
    }
""")

callback = CustomJS(args=dict(p=p), code="""
    var a = cb_obj.value;
    p.x_range.start = a[0];
    p.x_range.end = a[1];
""")

range_slider = DateRangeSlider(start=start_date, end=end_date,
                               value=(start_date, end_date), step=1)
range_slider.js_on_change('value', callback)


def get_hovertools():
    hovers = {'x': x, 'y': y, 'z': z}
    for k, v in hovers.items():
        hovers[k] = HoverTool(mode='vline', renderers=[v])
        hovers[k].tooltips = [('Date', '@Date{%F %H:%M:%S.%u}'),
                              (k, '@{'+k+'}{%0.2f}m')]
        hovers[k].formatters = {'Date': 'datetime', k: 'printf'}
        p.add_tools(hovers[k])


get_hovertools()
# --------------------- second figure here --------------------------
p2 = figure(title="data", plot_height=400, tools=TOOLS, plot_width=1300)

start_date = dt.datetime.strptime('2019 04 15', '%Y %m %d')
end_date = dt.datetime.strptime('2019 04 18', '%Y %m %d')
t = np.arange(0.0, 2.0, 0.01)
dates = np.arange(start_date, end_date, np.timedelta64(1, 'h'),
                  dtype='datetime64')

x2 = [1]*72
y2 = [2]*72
z2 = [3]*72

source = ColumnDataSource(data={'Date': dates, 'x': x2, 'y': y2, 'z': z2})


def add_plot(y, color):
    new_plot = p2.line(x='Date', y=y, line_width=1, color=color, source=source)
    return new_plot


x2 = add_plot('x', 'red')
y2 = add_plot('y', 'green')
z2 = add_plot('z', 'blue')

layout = column(p, widgetbox(checkbox), widgetbox(range_slider),
                p2)

show(layout)

此示例展示了如何在 Bokeh 文档 (Bokeh v1.1.0) 中添加和删除图形。出于清楚的原因,它不包含小部件,但您也可以使用相同的方法在其中添加小部件。

但是,也许您可​​以考虑将制表符作为一种选择。使用选项卡时,您不需要 remove/add 根元素,这些元素会持续出现在用户可以切换的单独选项卡上。您可以找到标签示例 here

from bokeh.models import Select, Row, ColumnDataSource
from bokeh.plotting import figure, curdoc
import numpy as np

x = np.linspace(0, 4 * np.pi, 100)
source = ColumnDataSource(dict(x = x, y = np.cos(x)))
glyphs = ["line", "scatter"]
select = Select(title = "Select plot:", value = "", options = [""] + glyphs)
curdoc().add_root(Row(select, name = 'root'))

def callback(attr, old, new):
    layouts = curdoc().get_model_by_name('root').children
    for glyph in glyphs:
        plot = curdoc().get_model_by_name(glyph)
        if plot:
            layouts.remove(plot)
    if new:
        p = figure(name = new)
        exec(new + ' = p.' + new + '("x", "y", source = source)')
        layouts.append(p)

select.on_change('value', callback)

结果: