Python 使用滑块更新Bokeh图

Python 使用滑块更新Bokeh图,python,bokeh,Python,Bokeh,我正在尝试使用滑块来更新我的Bokeh图。我发现使用pandas dataframe很难实现它(到目前为止还没有找到任何示例)。 另一种方法是使用“columndatasource”(在论坛上找到了一些示例),但仍然无法实现该功能。 我有两列,X轴是日期,Y轴是体积。我想根据滑块输入更改Y值。我可以看到绘图,但滑块功能不起作用 任何帮助都是非常值得的 source = ColumnDataSource(data=dict(x=df2['Date'],y=df2['Vol'])) S1 = fig

我正在尝试使用滑块来更新我的Bokeh图。我发现使用pandas dataframe很难实现它(到目前为止还没有找到任何示例)。 另一种方法是使用“columndatasource”(在论坛上找到了一些示例),但仍然无法实现该功能。 我有两列,X轴是日期,Y轴是体积。我想根据滑块输入更改Y值。我可以看到绘图,但滑块功能不起作用

任何帮助都是非常值得的

source = ColumnDataSource(data=dict(x=df2['Date'],y=df2['Vol']))
S1 = figure(plot_width=400,plot_height=400,tools=TOOLS1,title="Volume Per Day",x_axis_type="datetime")
S1.line('x','y',source=source)

callback_test = CustomJS(args=dict(source=source), code="""
var data = source.get('data');
var s_val = cb_obj.value
x = data['x']
y = data['y']
console.log(cb_obj) 
for (i = 0; i < s_val; i++) {
    y[i] = y[i]            
    }
source.trigger('change');
""")

slider = Slider(start=0, end= max_Vol, value=1, step=100,title="Vol Per Day",callback=callback_test)
source=ColumnDataSource(data=dict(x=df2['Date'],y=df2['Vol']))
S1=图形(绘图宽度=400,绘图高度=400,工具=TOOLS1,title=“每天的体积”,x轴类型=“日期时间”)
S1.行('x','y',源=源)
callback_test=CustomJS(args=dict(source=source),code=”“”
var data=source.get('data');
var s_val=cb_对象值
x=数据['x']
y=数据['y']
控制台日志(cb_obj)
对于(i=0;i
您正在尝试更新使用滑块绘制的数据范围

当您这样做时:

y = data['y']
for (i = 0; i < s_val; i++) {
    y[i] = y[i]            
    }
这只是将元素从0替换为s_val-1,不会更改列表的其余部分

你可以做两件事:

  • 直接更新显示的轴范围
  • 使用将根据滑块值从现有源填充的空源

source=ColumnDataSource(data=dict(x=df2['Date'],y=df2['Vol']))
fill_source=ColumnDataSource(data=dict(x=[],y=[]))
S1=图形(绘图宽度=400,绘图高度=400,工具=TOOLS1,title=“每天的体积”,x轴类型=“日期时间”)
S1.行('x','y',源=填充源)
callback\u test=CustomJS(args=dict(source=source,fill\u source=fill\u source),code=“”
var数据=source.data;
var fill_data=fill_source.data;
var s_val=cb_对象值;
填充数据['x']=[];
填充数据['y']=[];
对于(i=0;i
这里有一个工作示例,希望你能做到:)


使用bokeh serve filename.py运行代码这里是我为使其与bokeh last version一起工作所做的更改

JavaScript部分中的一些语法错误已经纠正,触发更改的方法现在是
change.emit
,独立文档的回调设置在滑块定义之后,这要归功于
js_on_change

我添加了所有的
import
命令来提供一个完整的示例 我还更改了可视化,仅显示滑块设置的飞行次数以下的数据(以便在向较低值移动滑块时更容易理解)

下面是生成的代码:

from bokeh.layouts import column, widgetbox
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Slider
from bokeh.plotting import Figure
import pandas as pd
from datetime import datetime, date, timedelta
from bokeh.plotting import show
from random import randint

today = date.today()
random_data = [[today + timedelta(days = i), randint(0, 10000)] for i in range(10)]
df2 = pd.DataFrame(random_data, columns = ['Date', 'Vol'])

source = ColumnDataSource(data = dict(x = df2['Date'], y = df2['Vol']))
fill_source = ColumnDataSource(data = dict(x = df2['Date'], y = df2['Vol'])) # set the graph to show all data at loading
TOOLS1 = []
S1 = Figure(plot_width = 400, plot_height = 400, tools = TOOLS1, title = "Volume Per Day", x_axis_type = "datetime")
S1.line('x', 'y', source = fill_source)

callback_test = CustomJS(args = dict(source = source, fill_source = fill_source), code = """
var data = source.data;
var fill_data = fill_source.data;
var s_val = cb_obj.value;
fill_data['x']=[];
fill_data['y']=[];
for (var i = 0; i <= data.x.length; i++) {   // added "var" declaration of variable "i"
        if (data['y'][i] <= s_val) { // more meaningful visualization: assuming you want to focuss on dates with less number of flights
            fill_data['y'].push(data['y'][i]); // [i] index on left side of assignment removed
        }
        else {
            fill_data['y'].push(0);
        }
        fill_data['x'].push(data['x'][i]);
    }
    fill_source.change.emit() ; // "trigger" method replaced by "change.emit"
""")

max_Vol = df2['Vol'].max()
slider = Slider(start = 0, end = max_Vol, value = max_Vol, step = 100, title = "Vol Per Day") # Remove attribute "callback = callback_test"
slider.js_on_change('value', callback_test) # new way of defining event listener

controls = widgetbox(slider)
layout = column(controls, S1)
show(layout)
来自bokeh.layouts导入列,widgetbox
从bokeh.models导入ColumnDataSource,CustomJS
从bokeh.models.widgets导入滑块
从bokeh.plotting导入图形
作为pd进口熊猫
从datetime导入datetime、date、timedelta
从bokeh.plotting导入显示
从随机导入randint
今天=日期。今天()
随机_数据=[[today+timedelta(days=i),randint(0,10000)]对于范围(10)内的i
df2=pd.DataFrame(随机_数据,列=['Date','Vol'])
source=ColumnDataSource(data=dict(x=df2['Date'],y=df2['Vol']))
fill#source=ColumnDataSource(data=dict(x=df2['Date'],y=df2['Vol']))#设置图表以显示加载时的所有数据
工具1=[]
S1=图形(绘图宽度=400,绘图高度=400,工具=TOOLS1,title=“每天的体积”,x轴类型=“日期时间”)
S1.行('x','y',源=填充源)
callback\u test=CustomJS(args=dict(source=source,fill\u source=fill\u source),code=“”
var数据=source.data;
var fill_data=fill_source.data;
var s_val=cb_对象值;
填充数据['x']=[];
填充数据['y']=[];

对于(var i=0;i和i从0到s_val),您只是将每个y[i]单独替换,因此没有任何变化。@Seb,知道如何实现它吗。.在您的帖子中,您说“我想根据滑块输入更改y值”。您希望y值如何更改?根据滑块值,数据帧的“Vol”“列被选中。例如,如果y=4000,则选择df2[vol]<4000并按时间绘制。这需要Bokeh服务器,因为OP尝试执行Javascript回调,这可能不是OP想要的。
source = ColumnDataSource(data=dict(x=df2['Date'],y=df2['Vol']))
fill_source = ColumnDataSource(data=dict(x=[],y=[]))
S1 = figure(plot_width=400,plot_height=400,tools=TOOLS1,title="Volume Per Day",x_axis_type="datetime")
S1.line('x','y',source=fill_source)

callback_test = CustomJS(args=dict(source=source,fill_source=fill_source), code="""
var data = source.data;
var fill_data = fill_source.data;
var s_val = cb_obj.value;
fill_data['x']=[];
fill_data['y']=[];
for (i = 0; i < s_val; i++) {
    fill_data['y'][i].push(data['y'][i]);
    fill_data['x'][i].push(data['x'][i]);          
    }
fill_source.trigger('change');
""")
from os.path import dirname, join
import pandas as pd
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import DateRangeSlider,DatePicker,DateFormatter, DataTable, TableColumn, NumberFormatter
from bokeh.io import curdoc
from datetime import datetime,date
import datetime
df = pd.read_csv(join(dirname(__file__), 'test.csv'))
df['dat'] = pd.to_datetime(df['date'])
source = ColumnDataSource(data=dict())

def update():
current = df[(df['dat'] >=  pd.to_datetime(slider.value[0])) & (df['dat'] <=  pd.to_datetime(slider.value[1]))]
  source.data = {
    'opens'             : current.open,
    'dates'           : current.date,
  }

slider = DateRangeSlider(title="Date Range: ", start=date(2010, 1, 1), end=date.today(), value=(date(2017, 9, 7),date.today()), step=1)
slider.on_change('value', lambda attr, old, new: update())




columns = [
   TableColumn(field="dates", title="Date" ,formatter=DateFormatter()),
    TableColumn(field="opens", title="open"),


data_table = DataTable(source=source, columns=columns, width=800)

controls = widgetbox(slider)
table = widgetbox(data_table)

curdoc().add_root(row(controls, table))

update()
date,open
951868800000,102
951955200000,100.5
952041600000,107.25
952300800000,109.94
952387200000,106
952473600000,103
952560000000,106.5
952646400000,107.62
952905600000,104
952992000000,107.75
953078400000,107.5
953164800000,109
953251200000,108.25
953510400000,110
953596800000,112.81
953683200000,114.5
953769600000,115.87
953856000000,115.37
954115200000,125
954201600000,125.75
954288000000,122.31
954374400000,118.87
954460800000,122.62
954720000000,120
954806400000,121.5
954892800000,120.5
954979200000,123.5
955065600000,123.5
955324800000,124.75
955411200000,121.62
955497600000,119.62
955584000000,112.5
955670400000,109.81
955929600000,103.87
956016000000,112.25
from bokeh.layouts import column, widgetbox
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Slider
from bokeh.plotting import Figure
import pandas as pd
from datetime import datetime, date, timedelta
from bokeh.plotting import show
from random import randint

today = date.today()
random_data = [[today + timedelta(days = i), randint(0, 10000)] for i in range(10)]
df2 = pd.DataFrame(random_data, columns = ['Date', 'Vol'])

source = ColumnDataSource(data = dict(x = df2['Date'], y = df2['Vol']))
fill_source = ColumnDataSource(data = dict(x = df2['Date'], y = df2['Vol'])) # set the graph to show all data at loading
TOOLS1 = []
S1 = Figure(plot_width = 400, plot_height = 400, tools = TOOLS1, title = "Volume Per Day", x_axis_type = "datetime")
S1.line('x', 'y', source = fill_source)

callback_test = CustomJS(args = dict(source = source, fill_source = fill_source), code = """
var data = source.data;
var fill_data = fill_source.data;
var s_val = cb_obj.value;
fill_data['x']=[];
fill_data['y']=[];
for (var i = 0; i <= data.x.length; i++) {   // added "var" declaration of variable "i"
        if (data['y'][i] <= s_val) { // more meaningful visualization: assuming you want to focuss on dates with less number of flights
            fill_data['y'].push(data['y'][i]); // [i] index on left side of assignment removed
        }
        else {
            fill_data['y'].push(0);
        }
        fill_data['x'].push(data['x'][i]);
    }
    fill_source.change.emit() ; // "trigger" method replaced by "change.emit"
""")

max_Vol = df2['Vol'].max()
slider = Slider(start = 0, end = max_Vol, value = max_Vol, step = 100, title = "Vol Per Day") # Remove attribute "callback = callback_test"
slider.js_on_change('value', callback_test) # new way of defining event listener

controls = widgetbox(slider)
layout = column(controls, S1)
show(layout)