Bokeh Multi-Select обратный вызов виджета не работает - PullRequest
0 голосов
/ 07 июня 2018

Я новичок в Боке.Попытка реализовать несколько обратных вызовов виджетов в последнее время и трудно найти какие-либо ресурсы в Интернете.

Сценарий: у меня есть виджет bokeh multi_select со списком компаний.Исходя из выбранных компаний, данные в Vbar должны быть изменены.Для этого я пытаюсь изменить источник данных, который используется в Vbar, на основе значений из Multi select.Я не могу получить желаемые результаты.Может кто-нибудь, пожалуйста, помогите мне с этим.

Я плохо разбираюсь в CustomJs и, следовательно, делаю это на Bokeh Server для callabcks.Если есть решение и для CustomJ, это мне очень поможет.

Большое спасибо за ваше время в Advance!

Ниже приведен код, который я использую

source =source1[source1['year']== dt.now().year]
sourcex = source[source['month'] ==1 & 
source['CompanyNo'].isin(['01','02','03','04','05','08']) ]

Overall= ColumnDataSource(source)
Curr= ColumnDataSource(sourcex)

boolinit = source['month']==1
view = CDSView(source=Overall, filters=[BooleanFilter(boolinit)])

hover3 = HoverTool(
            tooltips = [
                ('day', '@day'),
                ('ExtendedPrice','@{ExtendedPrice}{0,0}'),
                ],
            formatters = {
                'day': 'datetime',
                'ExtendedPrice': 'numeral'}
           )

p =  figure(
    title='YEARLY SALES',  
    plot_width=600, 
    plot_height=400, 
    min_border=3,

tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],  
toolbar_location="above")
p.vbar(x='day', top='ExtendedPrice', width=0.2, color='#e8bc76', 
source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'
p.yaxis[0].formatter = NumeralTickFormatter(format="0,0")



def Multi_Selectupdate(attrname, old, new):

    curr=sourcex[sourcex['CompanyNo'].isin(new)]
    source.data=curr.data




companies=['All']+sourcex['CompanyNo'].unique().tolist()
multi_select = MultiSelect(title="Select:", value=['01'], options=companies, 
height=200, width=100)

multi_select.on_change('value',Multi_Selectupdate )

layout = column(multi_select, p )

show(layout) 

1 Ответ

0 голосов
/ 08 июня 2018

Поскольку у меня нет данных, я сгенерировал их, используя следующий скрипт.Я предполагаю, что ваши данные имеют 3 столбца - Date, ExtendedPrice, CompanyNo.Сначала я сгенерировал случайные данные из 10К строк, а затем суммировал их на уровне CompanyNo, day, month, year.

import pandas as pd
import random
CopmanyList = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15']

df = pd.DataFrame({'base' : ["2017-01-01" for t in range(10000)],
    'Date' : [random.randint(0, 1035) for t in range(10000)], 
    'ExtendedPrice' : [random.random() for t in range(10000)],
    'CompanyNo' : [CopmanyList[random.randint(0, 15)] for t in range(10000)]})

df['base'] = pd.to_datetime(df['base'])
df["Date2"] = df.apply(lambda x: x["base"] + timedelta(days=x['Date']), axis=1)
df.drop(['base', 'Date'], axis=1, inplace=True)
df.set_index('Date2', inplace=True)
df['month'] = df.index.month
df['year'] = df.index.year
df['day'] = df.index.day
source1=df.groupby(['year','month','day', 'CompanyNo'], as_index = False)['ExtendedPrice'].sum()
source =source1[source1['year']== dt.now().year]
sourcex = source[source['month'] ==1]
sourcex = sourcex[sourcex['CompanyNo'].isin(['01'])]
sourcex.sort_values(by='day', inplace=True)

Теперь вы хотите, чтобыучитывая день и набор названий компаний, вам нужно опубликовать гистограмму с агрегацией некоторого типа ExtendedPrice, то есть, скажем, есть 2 строки для компании '01' и '02' (которые выбраны) и для месяца2 (который также выбирается ползунком) для текущего года, значение ExtendedPrice для этих двух значений, скажем, 100 и 200. В случае, если мы хотим показать сумму, необходимо отобразить 300 в виде диаграммы.Это резюме нужно вычислять динамически.

Есть два способа выполнить это.

1.Bokeh Callbacks

Это будет использовать собственные функции Python, но вам нужно запустить его с bokeh serve, например, bokeh serve --show <filename.py>.См. Приведенный ниже код: встроенная функция Python compsel фильтрует кадр данных pandas, суммирует его и заменяет данные бэкэнда диаграммы на вновь созданные данные -

from datetime import timedelta
from datetime import datetime as dt
from bokeh.models.widgets import MultiSelect, Slider
from bokeh.layouts import widgetbox, column
from bokeh.models.ranges import FactorRange
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, HoverTool, CustomJS

Overall= ColumnDataSource(source)
Curr= ColumnDataSource(sourcex[['day', 'ExtendedPrice']])
Curr.remove('index')

hover3 = HoverTool(tooltips = [('day', '@day'),('Sales','@{ExtendedPrice}{0,0.00}')],
                   formatters = {'day': 'datetime','Sales': 'numeral'})

p =  figure(title='YEARLY SALES',  plot_width=600, plot_height=400, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],  
toolbar_location="above")

r = p.vbar(x='day', top='ExtendedPrice', width=0.2, color='#e8bc76', source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'

def compsel(attr, old, new):
    vcomp = multi_select.value
    vmonth = slider.value
    sourcex = source[source['month'] ==vmonth]
    sourcex = sourcex[sourcex['CompanyNo'].isin(vcomp)]
    sourcex = sourcex.groupby(['day'], as_index = False)['ExtendedPrice'].sum()
    Currnew= ColumnDataSource(sourcex[['day', 'ExtendedPrice']])
    Currnew.remove('index')
    r.data_source.data=Currnew.data


companies=source['CompanyNo'].unique().tolist()
companies.sort()
multi_select = MultiSelect(title="Select:", value=['01'], options=companies, height=200, width=100)
slider = Slider(start=1, end=12, value=1, step=1, title="Month")
slider.on_change("value", compsel)
multi_select.on_change("value", compsel)

layout = column(multi_select, slider, p)

curdoc().add_root(layout)

Существуют и другие варианты размещения этого приложения.Посмотрите другой вариант, набрав bokeh serve --help в терминале

2.Обратные вызовы JavaScript

Это создаст функцию javascript для взаимодействия с диаграммой, которая не требует сервера bokeh и работает непосредственно в браузере.Это потребует очень простой код в JavaScript.В примере, решенном здесь, я суммирую ExtendedPrice как сумму, но код потребует небольших изменений для вычисления других характеристик, например среднего.Функция обратного вызова js здесь делает то же самое, что и функция обратного вызова bokeh, она читает большие наборы данных и фильтрует их на основе значений select и слайдера -

source =source1[source1['year']== dt.now().year]
sourcex = source[source['month'] ==1]
sourcex = sourcex[sourcex['CompanyNo'].isin(['01'])]
sourcex.sort_values(by='day', inplace=True)

Overall= ColumnDataSource(source)
Curr= ColumnDataSource(sourcex[['day', 'ExtendedPrice']])
Curr.remove('index')

hover3 = HoverTool(tooltips = [('day', '@day'),('Sales','@{ExtendedPrice}{0,0.00}')],
                   formatters = {'day': 'datetime','Sales': 'numeral'})

p =  figure(title='YEARLY SALES',  plot_width=600, plot_height=400, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],  
toolbar_location="above")

r = p.vbar(x='day', top='ExtendedPrice', width=0.2, color='#e8bc76', source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'

callms = CustomJS(args=dict(source=Overall, sc=Curr), code="""  
        var comp=msel.attributes.value;
        var f = slider.value;
        sc.data['ExtendedPrice'] = [];
        sc.data['day'] = [];
        for (var i = 0; i <= source.get_length(); i++){
          if (source.data['month'][i] == f){
            if (comp.indexOf(source.data['CompanyNo'][i]) >=0){
              var d1 = source.data['day'][i]
              if(typeof sc.data['day'][d1-1]=="undefined"){
                sc.data['day'][d1-1] = d1
                sc.data['ExtendedPrice'][d1-1] = source.data['ExtendedPrice'][i]
              }
              else{
                sc.data['ExtendedPrice'][d1-1] = sc.data['ExtendedPrice'][d1-1] + source.data['ExtendedPrice'][i]  
              }
            }
          }
        }
        sc.change.emit();
    """)

companies=source['CompanyNo'].unique().tolist()
companies.sort()
multi_select = MultiSelect(title="Select:", value=['01'], options=companies, height=200, width=100, callback=callms)
slider = Slider(start=1, end=12, value=1, step=1, title="Month", callback=callms)
callms.args["msel"] = multi_select
callms.args["slider"] = slider

layout = column(multi_select, slider, p)

output_file("Filterdata.html")
show(layout)
...