散景多选小部件回调不起作用

red*_*ddy 3 python callback multi-select python-3.x bokeh

我是Bokeh的新手。最近尝试实现一些小部件回调,并且很难在线找到任何资源。

场景:我有一个multi_select bokeh小部件,上面列出了公司列表。根据选择的公司,必须更改Vbar中的数据。为此,我试图基于“多选”中的值更改Vbar中使用的数据源。我无法获得理想的结果。有人可以帮我这个忙。

我在CustomJs方面很差,因此在Bokeh Server上执行Callabcks。如果在CustomJs上也有解决方案,那将对我有很大帮助。

非常感谢您抽出宝贵的时间!

下面是我正在使用的代码

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) 
Run Code Online (Sandbox Code Playgroud)

Ari*_*esh 6

由于我没有数据,因此使用以下脚本生成了数据。我假设你的数据有3列- ,Date,。ExtendedPrice CompanyNo我已经首先产生的10K行的随机数据,然后在总结它CompanyNodaymonthyear的水平。

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)
Run Code Online (Sandbox Code Playgroud)

现在,您要完成的工作是给定一天和一组公司名称,您需要发布某种类型的汇总条形图。 ExtendedPrice,即说公司'01'和'02'有两行(分别是选中),以及对于本年度的第2个月(也由滑块选择),这两个的ExtendedPrice值分别为100和200。如果要显示总和,则需要在条形图中显示300。此摘要,您需要动态计算。

有两种方法可以实现它。

1.散景回调

这将使用本机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)
Run Code Online (Sandbox Code Playgroud)

还有其他选项可以托管此应用程序。通过输入bokeh serve --help终端查看其他选项

2. JavaScript回调

这将生成一个JavaScript函数以与图表进行交互,这将不需要bokeh服务器,并且可以直接在浏览器上工作。这将需要非常简单的JavaScript代码。在这里解决的示例中,我总结ExtendedPrice为总和,但是代码将需要进行一些细微调整才能计算出其他统计数据,例如平均值。此处的js回调函数与bokeh回调函数具有相似的功能,它读取较大的数据集并根据选择和滑块值对其进行过滤-

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)
Run Code Online (Sandbox Code Playgroud)