使用Multiselect在Bokeh中启用行 [英] Enable lines in Bokeh using Multiselect

查看:137
本文介绍了使用Multiselect在Bokeh中启用行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在几个地方读出了带有时间和湿度数据的记录器。为了探索数据并分发它,我使用Python(通过jupyter notebook)和Bokeh。

为了简化数据探索,我希望能够启用和禁用带有时间和湿度数据的I读出记录器几个地方。为了探索数据并分发它,我使用Python(通过jupyter笔记本)和Bokeh。

为了简化数据探索,我希望能够启用和禁用位置图(以及将来的湿度)和/或温度)。要做到这一点,我想使用



我在github repo上添加了所有示例代码我的github repo ,以及javascript控制台输出。所有这些都是用IE11(公司限制)测试的。

解决方案

我找到了罪魁祸首,事实上确实是检查数组的键(0到len-1)而不是值

  res_str =\ 
if(multiselect.attributes.value.indexOf('%(loc)s')> -1){
%(loc)s_t.visible = true;
%(loc)s_rh .visible = true;
console.log('enable5%(loc)s');
} else {
%(loc)s_t.visible = false;
%( loc)s_rh.visible = false;
console.log('disabling5%(loc)s');
}
%({index:index,loc :location})

def generate_selector_code(locations)中的正确测试:



在查看散景示例,使用 CustomJS.from_coffe escript()并浏览 CoffeeScript文档

  of => in 
in =>没有JS等价


I read out loggers with time and humidity data on a few locations. To explore the data and distribute it I use Python (via jupyter notebook) and Bokeh.
To simplify the data exploration I want to be able to enable and disable the I read out loggers with time and humidity data on a few locations. To explore the data and distribute it I use Python (via jupyter notebook) and Bokeh.
To simplify the data exploration I want to be able to enable and disable the graphs of locations (and in the future also humidity and/or temperature). To do this I want to use multiselect.

I got so far as to select multiple lines, based on this post, but when I try it, it picks the first n locations, not the ones I selected.

Imports

import numpy as np
import itertools
from collections import OrderedDict

from bokeh.io import push_notebook, show, output_notebook, output_file
from bokeh.layouts import row
from bokeh.palettes import Set1_6
from bokeh.plotting import figure as bf
from bokeh.models import MultiSelect, CustomJS, Range1d, LinearAxis, ColumnDataSource
from bokeh.resources import CDN
output_notebook()

Helper functions

One function generates example data

def generate_example_data(x, param=1):   
    t = 20 + param + np.sin(x * (1 + param))
    rh = 50 + param + 10 * np.tan(x * (1 + param))
    return {"x": x.copy(), "t": t, "rh": rh}

Another function generates the javascript code to make a line visible or invisible. I tried a few ways, but none give correct results. I also added logging to be able to see what happens and which if-else clauses get triggered.

def generate_selector_code(locations):
    for index, location in enumerate(locations):
        res_str = """    if (%(index)i in multiselect.attributes.value) {
        %(loc)s_t.visible = true;
        %(loc)s_rh.visible = true;
        console.log('enabling0 %(loc)s' );
    } else {
        %(loc)s_t.visible = false;
        %(loc)s_rh.visible = false;
        console.log('disabling0 %(loc)s' );
    }
    if ('%(index)i' in multiselect.attributes.value) {
        %(loc)s_t.visible = true;
        %(loc)s_rh.visible = true;
        console.log('enabling1 %(loc)s' );
    } else {
        %(loc)s_t.visible = false;
        %(loc)s_rh.visible = false;
        console.log('disabling1 %(loc)s' );
    }
    if ('%(loc)s' in multiselect.attributes.value) {
        %(loc)s_t.visible = true;
        %(loc)s_rh.visible = true;
        console.log('enabling2 %(loc)s' );
    } else {
        %(loc)s_t.visible = false;
        %(loc)s_rh.visible = false;
        console.log('disabling2 %(loc)s' );
    }
    """%({"index": index, "loc": location})
# other method's I've tested but which result into an error which states that Object does not have an attribute includes
#     if (multiselect.attributes.value.includes('%(index)i')) {
#         %(loc)s_t.visible = true;
#         %(loc)s_rh.visible = true;
#         console.log('enabling3 %(loc)s' );
#     } else {
#         %(loc)s_t.visible = false;
#         %(loc)s_rh.visible = false;
#         console.log('disabling3 %(loc)s' );
#     }
#     if (multiselect.attributes.value.includes('%(loc)s')) {
#         %(loc)s_t.visible = true;
#         %(loc)s_rh.visible = true;
#         console.log('enabling4 %(loc)s' );
#     } else {
#         %(loc)s_t.visible = false;
#         %(loc)s_rh.visible = false;
#         console.log('disabling4 %(loc)s' );
#     }

        yield res_str

Setting up the plot

Generates the example data and selects which tools to use

locations = ["loc_one", "loc_two", "loc_three"]
x = np.linspace(0, 4 * np.pi, 20)
data_per_loc = OrderedDict()
for i, loc in enumerate(locations):
    data_per_loc[loc] = generate_example_data(x, i)

tools="pan,box_zoom,reset,resize,save,crosshair,hover,xbox_zoom, wheel_zoom"

Generating the plot

The function which does the actual plot generation. It creates the actual Bokeh figure and concatenates the javascript code to enable or disable the different lines

def generate_plot(data_per_loc):
    palet = itertools.cycle(Set1_6)
    p = bf(title="test", plot_height=500, plot_width=1000, tools=tools, y_range=(17, 27), 
       toolbar_location="above")
    p.xaxis.axis_label = "x"

    p.yaxis.axis_label = "Temperature [°C]"
    p.extra_y_ranges = {"humidity": Range1d(start=30, end=80)}
    p.add_layout(LinearAxis(y_range_name="humidity", axis_label="Relative Humidity [%Rh]"), 'right')

    plot_locations = OrderedDict()
    for location, datadict in data_per_loc.items():
        colour = next(palet)
        source = ColumnDataSource(datadict)
        t = p.line(x='x', y='t', color=colour, source=source, legend=location)
        rh = p.line(x='x', y='rh', source=source, color=colour,
               legend=location, y_range_name='humidity',
               line_dash="dashed", )
        plot_locations.update({location+"_t": t, location+"_rh": rh})

    code = "console.log('value: ' + multiselect.attributes.value);\n " + "console.log('value_type: ' + Object.prototype.toString.call(multiselect.attributes.value).slice(8, -1));\n " +             "console.log('options: ' + multiselect.attributes.options);\n " + "".join(generate_selector_code(data_per_loc.keys()))
    return p, code, plot_locations

The resulting code looks like this:

"console.log('value: ' + multiselect.attributes.value);
 console.log('value_type: ' + Object.prototype.toString.call(multiselect.attributes.value).slice(8, -1));
 console.log('options: ' + multiselect.attributes.options);
 if (0 in multiselect.attributes.value) {
    loc_one_t.visible = true;
    loc_one_rh.visible = true;
    console.log('enabling0 loc_one' );
} else {
    loc_one_t.visible = false;
    loc_one_rh.visible = false;
    console.log('disabling0 loc_one' );
}
if ('0' in multiselect.attributes.value) {
    loc_one_t.visible = true;
    loc_one_rh.visible = true;
    console.log('enabling1 loc_one' );
} else {
    loc_one_t.visible = false;
    loc_one_rh.visible = false;
    console.log('disabling1 loc_one' );
}
if ('loc_one' in multiselect.attributes.value) {
    loc_one_t.visible = true;
    loc_one_rh.visible = true;
    console.log('enabling2 loc_one' );
} else {
    loc_one_t.visible = false;
    loc_one_rh.visible = false;
    console.log('disabling2 loc_one' );
}
    if (1 in multiselect.attributes.value) {
    loc_two_t.visible = true;
    loc_two_rh.visible = true;
    console.log('enabling0 loc_two' );
} else {
    loc_two_t.visible = false;
    loc_two_rh.visible = false;
    console.log('disabling0 loc_two' );
}
if ('1' in multiselect.attributes.value) {
    loc_two_t.visible = true;
    loc_two_rh.visible = true;
    console.log('enabling1 loc_two' );
} else {
    loc_two_t.visible = false;
    loc_two_rh.visible = false;
    console.log('disabling1 loc_two' );
}
if ('loc_two' in multiselect.attributes.value) {
    loc_two_t.visible = true;
    loc_two_rh.visible = true;
    console.log('enabling2 loc_two' );
} else {
    loc_two_t.visible = false;
    loc_two_rh.visible = false;
    console.log('disabling2 loc_two' );
}
    if (2 in multiselect.attributes.value) {
    loc_three_t.visible = true;
    loc_three_rh.visible = true;
    console.log('enabling0 loc_three' );
} else {
    loc_three_t.visible = false;
    loc_three_rh.visible = false;
    console.log('disabling0 loc_three' );
}
if ('2' in multiselect.attributes.value) {
    loc_three_t.visible = true;
    loc_three_rh.visible = true;
    console.log('enabling1 loc_three' );
} else {
    loc_three_t.visible = false;
    loc_three_rh.visible = false;
    console.log('disabling1 loc_three' );
}
if ('loc_three' in multiselect.attributes.value) {
    loc_three_t.visible = true;
    loc_three_rh.visible = true;
    console.log('enabling2 loc_three' );
} else {
    loc_three_t.visible = false;
    loc_three_rh.visible = false;
    console.log('disabling2 loc_three' );
}
"

First attempt at bringing it together

output_file("c:\html\multiselect_loc.html")
p, code, plot_locations = generate_plot(data_per_loc)

ms_options = locations
ms_value = locations

callback = CustomJS(code=code, args={})
multiselect = MultiSelect(title="Location:", options=ms_options, value=ms_value, callback=callback)
callback.args = dict(**plot_locations, multiselect=multiselect)


layout = row(p, multiselect)
show(layout)

Second attempt at bringing it together

I thought perhaps the javascript had problems with the strings as values, so I tried using ints as values for the multiselect

output_file("c:\html\multiselect_val.html")
p, code, plot_locations = generate_plot(data_per_loc) 

ms_options = [(str(i), v) for i , v in enumerate(locations)]
ms_value = [str(i) for i in range(len(locations))]

callback = CustomJS(code=code, args={})
multiselect = MultiSelect(title="Location:", options=ms_options value=ms_value, callback=callback)
callback.args = dict(**plot_locations, multiselect=multiselect)


layout = row(p, multiselect)
show(layout)

The results

Bokeh plotted the lines without problems, but the selection acted weird. Instead of showing the locations I wanted, it gave the first n lines with first 2 selection methods, and nothing with the 3rd.

The javascript console returns something like:

value: loc_two
value_type: Array
options: loc_one,loc_two,loc_three
enabling0 loc_one
enabling1 loc_one
disabling2 loc_one
disabling0 loc_two
disabling1 loc_two
disabling2 loc_two
disabling0 loc_three
disabling1 loc_three
disabling2 loc_three
value: loc_two,loc_three
value_type: Array
options: loc_one,loc_two,loc_three
enabling0 loc_one
enabling1 loc_one
disabling2 loc_one
enabling0 loc_two
enabling1 loc_two
disabling2 loc_two
disabling0 loc_three
disabling1 loc_three
disabling2 loc_three

I've added all example code on my github repo my github repo, as well as the javascript console outputs. All this was tested with IE11 (company restrictions).

解决方案

I've found the culprit, and it is indeed the fact in checks in the keys of the array (0 to len-1) instead of the values

    res_str = """\
if (multiselect.attributes.value.indexOf('%(loc)s')>-1) {
    %(loc)s_t.visible = true;
    %(loc)s_rh.visible = true;
    console.log('enabling5 %(loc)s' );
} else {
    %(loc)s_t.visible = false;
    %(loc)s_rh.visible = false;
    console.log('disabling5 %(loc)s' );
}
"""%({"index": index, "loc": location})

is the correct test in def generate_selector_code(locations):

I noticed this when reviewing the code in the bokeh example which uses CustomJS.from_coffeescript() and browsing throught the CoffeeScript documentation

of  => in 
in  => no JS equivalent 

这篇关于使用Multiselect在Bokeh中启用行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆