Как написать код Javascript для части фильтра обратного вызова Bokeh, чтобы я мог фильтровать как по количеству, так и по категориям? - PullRequest
1 голос
/ 18 января 2020

Я создаю визуализацию Bokeh и застрял с Javascript. Я настроил некоторый код JS для работы со слайдером, и я также хочу дополнительно отфильтровать эти результаты по категориям с помощью флажка.

Я пытался получить то, что у меня есть (упрощенно версия ниже) для работы с другим кодом, который я нашел на , подобном этому другому сообщению stackoverflow , но я понятия не имею, как расширить его после первого JS для l oop. Я бы включил то, что готовил, но, честно говоря, это бессмысленный мусор.

from bokeh.models import Slider, CustomJSFilter, CDSView, ColumnDataSource, CustomJS
from bokeh.models.widgets import CheckboxGroup
from bokeh.layouts import column, layout

data = dict(Flights=[97, 34, 23, 6, 26, 97, 21, 92, 73, 10, 92, 14, 77, 4, 25, 48, 26, 39, 93],
            Not_Cancelled=[87, 63, 56, 38, 57, 63, 73, 56, 30, 23, 66, 47, 76, 15, 80, 78, 69, 87, 28],
            OnTime_Arrivals=[21, 65, 86, 39, 32, 62, 46, 51, 17, 79, 64, 43, 54, 50, 47, 63, 54, 84, 79],
            Category = ['A', 'B', 'B', 'C', 'A', 'C', 'B', 'C', 'C', 'B', 'A', 'A', 'B', 'B', 'A', 'C', 'C', 'C', 'C'])
source = ColumnDataSource(data=data)
Category_dict = dict(Cat = ['A','B','C'])
MinFlights = Slider(start=0, value=50, end=100, step=1)
MinFlights.js_on_change('value', CustomJS(args=dict(source=source), code="""
   source.change.emit()
"""))
checkbox_group = CheckboxGroup(labels= list(Category_dict["Cat"]), active = [1])
checkbox_group.js_on_change("active", CustomJS(code="source.change.emit();", args=dict(source=source)))

custom_filter = CustomJSFilter(args=dict(source=source, MinFlights=MinFlights), code='''
    var indices = [];
    for (var i = 0; i < source.get_length(); i++){
        if (source.data['Flights'][i] > MinFlights.value){
            indices.push(true);
        } else {
            indices.push(false);}}
    return indices;
''')
view = CDSView(source=source, filters=[custom_filter])

p = figure()
p.circle('OnTime_Arrivals', 'Not_Cancelled', source=source, view=view, size=20)

inputs = column(MinFlights, checkbox_group, width=200)
show(layout([[inputs,p]]))

Может кто-нибудь помочь мне? Все, что мне нужно, это некоторый код, демонстрирующий, как это работает, и я могу адаптировать остальное.

Ответы [ 2 ]

0 голосов
/ 23 января 2020

Один из способов реализовать это - добавить checkbox_group=checkbox_group, categories=categories к вашему CustomJSFilter обратному вызову, чтобы он знал текущий выбор категорий. Затем вы добавляете второй этап фильтрации на indices, чтобы в конечном итоге вернуть массив selected в ваш обратный вызов (работает для Bokeh v 1.3.0).

from bokeh.models import Slider, CustomJSFilter, CDSView, ColumnDataSource, CustomJS, CheckboxGroup, Column, Row
from bokeh.plotting import figure, show

data = dict(Flights=[97, 34, 23, 6, 26, 97, 21, 92, 73, 10, 92, 14, 77, 4, 25, 48, 26, 39, 93],
            Not_Cancelled=[87, 63, 56, 38, 57, 63, 73, 56, 30, 23, 66, 47, 76, 15, 80, 78, 69, 87, 28],
            OnTime_Arrivals=[21, 65, 86, 39, 32, 62, 46, 51, 17, 79, 64, 43, 54, 50, 47, 63, 54, 84, 79],
            Category = ['A', 'B', 'B', 'C', 'A', 'C', 'B', 'C', 'C', 'B', 'A', 'A', 'B', 'B', 'A', 'C', 'C', 'C', 'C'])

source = ColumnDataSource(data=data)

categories = ['A','B','C']

MinFlights = Slider(start=0, value=50, end=100, step=1)
MinFlights.js_on_change('value', CustomJS(args=dict(source=source), code="""
   source.change.emit()
"""))

checkbox_group = CheckboxGroup(labels= categories, active = [0, 1, 2])
checkbox_group.js_on_change("active", CustomJS(code="source.change.emit();", args=dict(source=source)))

custom_filter = CustomJSFilter(args=dict(source=source, MinFlights=MinFlights, checkbox_group=checkbox_group, categories=categories), code='''
    var indices = [];
    for (var i = 0; i < source.get_length(); i++){
        if (source.data['Flights'][i] > MinFlights.value){
            indices.push(true);
        } else {
            indices.push(false);}}

    selected = []
    for (i in indices) {
        let selected_categories = checkbox_group.active.map((i) => categories[i])
        console.log(selected_categories)
        console.log(i, indices[i], source.data['Category'][i], selected_categories.includes(source.data['Category'][i]))
        if(indices[i] && selected_categories.includes(source.data['Category'][i])) {
            selected.push(true)
        }
        else {
            selected.push(false)
        }
    }
    return selected;
''')
view = CDSView(source=source, filters=[custom_filter])

p = figure()
p.circle('OnTime_Arrivals', 'Not_Cancelled', source=source, view=view, size=20)

inputs = Column(MinFlights, checkbox_group, width=200)
show(Row(inputs, p))

enter image description here

0 голосов
/ 18 января 2020

Я не эксперт по боке, но мне кажется, что ваша проблема может быть решена с помощью "BooleanFilter".

Преимущество: вы можете написать python код с помощью booleanFilter и не иметь беспокоиться о JS

Например, я бы что-то вроде этого:

import pandas as pd, numpy as np
from bokeh.models import BooleanFilter,ColumnDataSource,CDSView

df=pd.DataFrame(data)
df["bools"]=np.where((df.Flights > 10)&(df.Category=="A"),True,False)
view = CDSView(source=ColumnDataSource(df), filters=[BooleanFilter(bools)])
...