【问题标题】:Bokeh - Update plot with Select widget and CustomJSBokeh - 使用 Select 小部件和 CustomJS 更新绘图
【发布时间】:2020-04-13 22:26:24
【问题描述】:

我正在尝试显示条形图并通过 Select 对象过滤内容。看起来很简单,经过两天的寻找,我还没有找到一个可行的解决方案。我需要用 CustomJS 来做这件事,而不是散景服务器。

这是我正在尝试的代码,但是当我运行它时,什么都没有显示,甚至没有一个空图。

import pandas as pd
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CustomJS, CustomJSFilter, CDSView, Select, IndexFilter
from bokeh.io import show, output_notebook
from bokeh.layouts import column
output_notebook()

df = pd.DataFrame({'Subject': ['Math', 'Math', 'Math', 'Science', 'Science', 'Science'],
                  'Class': ['Algebra', 'Calculus', 'Trigonometry', 'Biology', 'Chemistry', 'Physics'],
                  'FailRate': [0.05, 0.16, 0.31, 0.12, 0.20, 0.08]})

src = ColumnDataSource(df)

subj_list = sorted(list(set(src.data['Subject'])))

callback = CustomJS(args=dict(src=src), code='''
    src.change.emit();
''')

js_filter = CustomJSFilter(code='''
var indices = [];
for (var i = 0; i < src.get_length(); i++){
    if (src.data['Subject'][i] == select.value){
        indices.push(true);
    } else {
        indices.push(false);
    }
}
return indices;
''')

options = ['Please select...'] + subj_list
select = Select(title='Subject Selection', value=options[0], options=options)

select.js_on_change('value', callback)

view = CDSView(source=src, filters=[js_filter])

class_list = sorted(list(src.data['Class']))

p = figure(x_range=class_list, plot_height=400, plot_width=400) 
p.vbar('Class', top='FailRate', width=0.9, source=src, view=view)

show(column(select, p))

据我所知,部分问题涉及这一行(或“视图”变量):

p.vbar('Class', top='FailRate', width=0.9, source=src, view=view)

在我将“view=view”添加到上述行之前,我至少得到了一个选择框并显示了绘图,即使交互不起作用。

【问题讨论】:

    标签: javascript python pandas callback bokeh


    【解决方案1】:

    Bokeh 可以神奇地跨运行时传输您的 Python 对象,以在浏览器中显示为 JavaScript 对象,但这种魔力是有限度的。您必须通过向CustomJS 对象提供args 参数来准确地告诉Bokeh 哪些 对象要传输。此外,如果您使用回调来更新 IndexFilter 中的索引,这会更简单。那么你只需要一个回调:

    filter = IndexFilter(indices=[])
    
    callback = CustomJS(args=dict(src=src, filter=filter), code='''
      const indices = []
      for (var i = 0; i < src.get_length(); i++) {
        console.log(i, src.data['Subject'][i], cb_obj.value)
        if (src.data['Subject'][i] == cb_obj.value) {
          indices.push(i)
        }
      }
      filter.indices = indices
      src.change.emit()
    ''')
    
    select.js_on_change('value', callback)
    
    view = CDSView(source=src, filters=[filter])
    

    最后一点:我没有传递和使用select,而是使用了隐式cb_obj 变量,该变量始终是触发更改的对象。 (使用传入的选择可能存在错误)

    编辑:不,没有错误。问题是Select 是在回调之后定义的,并且由于笔记本中的内容,它使用的是先前单元执行的值。不幸的是,笔记本的固有特性使得损坏状态的问题通常很容易实现。如果您首先定义Select,那么代码将按预期使用select 而不是cb_obj

    options = ['Please select...'] + subj_list
    select = Select(title='Subject Selection', value=options[0], options=options)
    
    cb = CustomJS(args=dict(select=select, src=src, filter=filter), ...)
    

    即使是重复的单元格调用。请注意,cb_obj 没有任何问题,但许多人更喜欢显式传递(和命名)小部件。

    作为最后一点,我会提到学习检查浏览器的 JS 控制台对于调试CustomJS 类型的回调是必不可少的。您的代码中的错误立即出现在那里。

    【讨论】:

    • 谢谢,这让我更接近,但结果有两个问题。第一个是初始图(尚未进行选择时)出现但为空白,并且在网格的左上角塞满了一堆小文本。第二个问题是 x_range 值没有得到更新,所以我在 x 轴上仍然有从图中过滤掉的项目的刻度。
    • 您需要明确设置y_range(我在测试时使用了y_range=(0,1))当没有数据要自动排列时,散景无法自动排列,并且您将其设置为从空视图开始。或者,您可以选择从其中一个类别开始,以便一开始就有数据。范围问题是一个单独的问题。请打开一个新主题(在 Discourse 上)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-06
    • 2018-05-11
    • 1970-01-01
    • 2018-04-22
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多