简体   繁体   English

在 Python 的 Bokeh 中使用 Javascript 回调过滤数据

[英]Filter data with Javascript callback in Python's Bokeh

apologies in advance for unprecise/unappreciated wording as this is my first question here.提前为不准确/不受欢迎的措辞道歉,因为这是我在这里的第一个问题。 Feel free to point out how I can improve it in the future.请随时指出我将来如何改进它。

I have been reading through all of Bokeh's user guide and various forums but belief this question is still insufficiently covered as it appears over and over again without an answer that can be applied generically.我一直在阅读 Bokeh 的所有用户指南和各种论坛,但我相信这个问题仍然没有得到充分涵盖,因为它一遍又一遍地出现而没有可以通用的答案。

My task is to construct a scatterplot in Python's Bokeh that can interactively be filtered based on a categorical variable.我的任务是在 Python 的 Bokeh 中构建一个散点图,该散点图可以根据分类变量进行交互过滤。 My limited understanding of Javascript (and how the data is structured) prevents me from figuring this out by myself.我对 Javascript(以及数据的结构)的理解有限,这使我无法自己解决这个问题。

I found, that one solution is to append x&y values that fulfill the condition (fe Filtering Bokeh LabelSet with Javascript ).我发现,一种解决方案是附加满足条件的 x&y 值(fe Filtering Bokeh LabelSet with Javascript )。 However, I want to keep all the other variables as well, since I use them to define graphic parameters / hover information in the plot.但是,我也想保留所有其他变量,因为我使用它们来定义绘图中的图形参数/悬停信息。

Therefore my question, how can I append whole rows to the new output data if one of the columns fulfills a certain condition in Javascript?因此,我的问题是,如果其中一列满足 Javascript 中的某个条件,我如何将整行附加到新的输出数据中? I am also unsure if I call the callback correctly such that the plot would actually react to my selection.我也不确定我是否正确调用了回调,以便情节实际上对我的选择做出反应。 So please fell free to point out any mistakes here as well.所以请随意指出这里的任何错误。

See some example code here:在此处查看一些示例代码:

#Packages
import pandas as pd
import numpy as np
from bokeh.plotting import figure, output_file, show
import bokeh.events as bev
import bokeh.models as bmo
import bokeh.layouts as bla

#Data
data = pd.DataFrame(data = np.array([[1,1,'a',0.5],
                                     [2,2,'a',0.5],
                                     [3,3,'a',0.75],
                                     [4,4,'b',1],
                                     [5,5,'b',2]]),
                    columns = ['x', 'y', 'category', 'other information'])


#Setup
output_file('dashboard.html')

source = bmo.ColumnDataSource(data)

#Define dropdown options
dropdown_options = [('All', 'item_1'), None] + [(cat, str('item_' + str(i))) for i, cat in enumerate(sorted(data['category'].unique()), 2)]

#Generate dropdown widget
dropdown = bmo.Dropdown(label = 'Category', button_type = 'default', menu = dropdown_options)


#Callback
callback = bmo.CustomJS(args = dict(source = source),
                        code = """
                        
                        var data = source.data;
                        
                        var cat = cb_obj.value;
                        
                        if (cat = 'All'){
                                
                            data = source.data
                                
                        } else {
                            
                            var new_data = [];
                            
                            for (cat i = 0; i <= source.data['category'].length; i++){
                                    
                                    if (source.data['category'][i] == cat) {
                                            
                                            new_data.push(source.data[][i])
                                            
                                            }
                                    
                                    }
                            
                            data = new_data.data
                                                    
                        }
                            
                        source.data = data
                                                  
                        source.change.emit();
                        
                        """)


#Link actions
dropdown.js_on_event(bev.MenuItemClick, callback)

#Plot
p = figure(plot_width = 800, plot_height = 530, title = None)

p.scatter(x = 'x', y = 'y', source = source)


show(bla.column(dropdown, p))

Unsurprisingly, the filter does not work.不出所料,过滤器不起作用。 As said, any help highly appreciated since I do not know how to index whole rows in Javascript and whatever else I am doing wrong.如前所述,任何帮助都受到高度赞赏,因为我不知道如何在 Javascript 中索引整行以及我做错了什么。

Best regards, Oliver最好的问候,奥利弗

I wrote a solution for your issue.我为您的问题写了一个解决方案。 I am no Bokeh expert so I might not know everything but hope that helps to understand what is going on.我不是散景专家,所以我可能不知道一切,但希望有助于了解正在发生的事情。 Some explanation:一些解释:

  • You had some syntax errors to start with: at your for loop you used cat i , you probably meant var i您开始时有一些语法错误:在您的 for 循环中,您使用了cat i ,您可能指的是var i

  • In your if you were assigning All to cat , you need to do the comparison: with either cat == 'All' or cat === 'All'如果您将All分配给cat ,则需要进行比较:使用cat == 'All'cat === 'All'

  • your cb_obj.value did not work for some reason and was returning undefined.您的cb_obj.value由于某种原因不起作用,并且返回未定义。 You can check your variables with simple console.log(variableName) and open dev console in the browser to see callbacks in action.您可以使用简单的console.log(variableName)检查变量并在浏览器中打开开发控制台以查看回调。 I changed your list comprehension to be tuple of the same values instead of (category_name, item_category_number) .我将您的列表理解更改为相同值的元组,而不是(category_name, item_category_number) Now cb_obj.item returns category_name which you can do comparison with.现在cb_obj.item返回category_name ,您可以与之进行比较。

  • You should understand what format your data is in, you can do so with console.log(source.data) for example.您应该了解数据的格式,例如,您可以使用console.log(source.data)来做到这一点。 source.data here is object of arrays (or dictionary of lists if you were to describe that in Python).这里的source.data是数组的对象(或者是列表的字典,如果你source.data Python 来描述的话)。 Because of that you could not push the data the way you did in for loop and also you had a syntax error: source.data[][i] - you won't access what you want with empty bracket.因此,您无法像在 for 循环中那样推送数据,并且出现语法错误: source.data[][i] - 您将无法使用空括号访问您想要的内容。 I wrote two functions to handle this functionality.我写了两个函数来处理这个功能。 generateNewDataObject creates object of empty arrays that we can append with addRowToAccumulator generateNewDataObject创建空数组的对象,我们可以用addRowToAccumulator附加addRowToAccumulator

  • The last thing is that I needed were two data_sources.最后一件事是我需要两个 data_sources。 First that we will not do changes on and second that we will modify and use to display on the plot.首先我们不会对其进行更改,其次我们将修改并用于在绘图上显示。 If we were to modify the first one then after the first filter all other categories would be dropped and we could get them back only by refreshing the page.如果我们要修改第一个,那么在第一个过滤器之后,所有其他类别都将被删除,我们只能通过刷新页面来恢复它们。 The 'immutable' data_source allows us to reference it and not lose filtered data in the process. 'immutable' data_source 允许我们引用它并且不会在过程中丢失过滤的数据。

I hope that helps.我希望这有帮助。

# Packages

import bokeh.events as bev
import bokeh.layouts as bla
import bokeh.models as bmo
import numpy as np
import pandas as pd
from bokeh.plotting import figure, output_file, show

# Data
data = pd.DataFrame(
    data=np.array(
        [
            [1, 1, 'a', 0.5],
            [2, 2, 'a', 0.5],
            [3, 3, 'a', 0.75],
            [4, 4, 'b', 1],
            [5, 5, 'b', 2]
        ]
    ),
    columns=['x', 'y', 'category', 'other information']
)

# Setup
output_file('dashboard.html')

source = bmo.ColumnDataSource(data)

# Define dropdown options
dropdown_options = [
                       ('All', 'All'), None
                   ] + [(cat, cat)
                       for i, cat in enumerate(sorted(data['category'].unique()), 2)
                   ]
# Generate dropdown widget
dropdown = bmo.Dropdown(label='Category', button_type='default', menu=dropdown_options)

filtered_data = bmo.ColumnDataSource(data)
# Callback
callback = bmo.CustomJS(
    args=dict(unfiltered_data=source, filtered_data=filtered_data),
    code="""

var data = unfiltered_data.data;
var cat = cb_obj.item;

function generateNewDataObject(oldDataObject){
    var newDataObject = {}
    for (var key of Object.keys(oldDataObject)){
        newDataObject[key] = [];
    }
    return newDataObject

}

function addRowToAccumulator(accumulator, dataObject, index) {
    for (var key of Object.keys(dataObject)){
        accumulator[key][index] = dataObject[key][index];
    }
    return accumulator;
}

if (cat === 'All'){
    data = unfiltered_data.data;
} else {
    var new_data =  generateNewDataObject(data);
    for (var i = 0; i <= unfiltered_data.data['category'].length; i++){
        if (unfiltered_data.data['category'][i] == cat) {
            new_data = addRowToAccumulator(new_data, unfiltered_data.data, i);
        }
    }
    data = new_data;
}

filtered_data.data = data;
filtered_data.change.emit();
"""
)

# Link actions
dropdown.js_on_event(bev.MenuItemClick, callback)

# Plot
p1 = figure(plot_width=800, plot_height=530, title=None)

p1.scatter(x='x', y='y', source=filtered_data)

show(bla.column(dropdown, p1))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Python的Bokeh的JavaScript回调中更新源? - How to update a source in a JavaScript Callback of Bokeh in Python? 散景 Python CustomJS 回调更新 Select 小部件过滤散点图 - Bokeh Python CustomJS Callback Update Select Widget to filter Scatterplot 散景,回调中的更改数据不会更改原始python数据 - bokeh, change data in callback does not change original python data Python Bokeh CustomJS:调试 Taping-Tool 的 JavaScript 回调 - Python Bokeh CustomJS: Debugging a JavaScript callback for the Taping-Tool Python,Bokeh,Javascript:在回调中使用Mongodb数据库查询 - Python, Bokeh, Javascript: use Mongodb database query in callback 如何为 Bokeh 回调过滤器的一部分编写 Javascript 代码,以便我可以按数字和类别进行过滤? - How to write Javascript code for part of Bokeh callback filter, so that I can filter by both number and categories? 为bokeh中的复选框实现JavaScript回调? - Implementing JavaScript callback for checkboxes in bokeh? 如何在Bokeh中为多选小部件实现Javascript回调 - How to implement a Javascript callback for a multiselect widget in Bokeh JavaScript回调以获取Bokeh中的选定字形索引 - JavaScript callback to get selected glyph index in Bokeh Python bokeh CustomJS回调更新DataTable小部件 - Python bokeh CustomJS callback update DataTable widget
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM