为了账号安全,请及时绑定邮箱和手机立即绑定

Bokeh自定义JS回调日期范围滑块

Bokeh自定义JS回调日期范围滑块

翻过高山走不出你 2024-01-27 15:25:58
我尝试创建一个交互式图表,绘制值 Y 与日期 X 的关系。到目前为止一切顺利。现在我想通过 DateRangeSlider 调整 x 轴的限制 xmin 和 xmax 但我不理解 js 回调函数(我想在最后有一个独立的 html 文件),因为我什至不知道如何从函数内部打印值并且不产生任何错误,我不知道现在该怎么办。这是代码的运行示例:import numpy as npimport pandas as pdfrom datetime import datetimefrom bokeh.models import ColumnDataSource, DatetimeTickFormatter, HoverToolfrom bokeh.models.widgets import DateRangeSliderfrom bokeh.layouts import layout, columnfrom bokeh.models.callbacks import CustomJSfrom bokeh.plotting import figure, output_file, show, savedatesX = pd.date_range(start='1/1/2018', periods=100)valuesY = pd.DataFrame(np.random.randint(0,25,size=(100, 1)), columns=list('A'))source = ColumnDataSource(data={'x': datesX, 'y': valuesY['A']}) # output to static HTML fileoutput_file('file.html')hover = HoverTool(tooltips=[('Timestamp', '@x{%Y-%m-%d %H:%M:%S}'), ('Value', '@y')],                           formatters={'x': 'datetime'},)    date_range_slider = DateRangeSlider(title="Zeitrahmen", start=datesX[0], end=datesX[99], \                                        value=(datesX[0], datesX[99]), step=1, width=300)# create a new plot with a title and axis labelsp = figure(title='file1', x_axis_label='Date', y_axis_label='yValue',  x_axis_type='datetime',                tools="pan, wheel_zoom, box_zoom, reset", plot_width=300, plot_height=200)# add a line renderer with legend and line thickness    p.line(x='x', y='y', source=source, line_width=2)p.add_tools(hover)       callback = CustomJS(args=dict(source=source), code="""    ##### what to do???    source.change.emit();    """)    date_range_slider.js_on_change('value', callback)layout = column(p, date_range_slider)# show the resultsshow(layout)
查看完整描述

4 回答

?
慕沐林林

TA贡献2016条经验 获得超9个赞

source.data更改滑块时需要创建一个新的。为此,您还需要一个不可source更改的“备份” ,并作为要包含哪些数据的参考。将两者作为参数传递给回调函数使它们可用于 Javascript 代码。


datesX = pd.date_range(start='1/1/2018', periods=100)

valuesY = pd.DataFrame(np.random.randint(0,25,size=(100, 1)), columns=list('A'))


# keep track of the unchanged, y-axis values

source = ColumnDataSource(data={'x': datesX, 'y': valuesY['A']}) 

source2 = ColumnDataSource(data={'x': datesX, 'y': valuesY['A']})


# output to static HTML file

output_file('file.html')


hover = HoverTool(

    tooltips=[('Timestamp', '@x{%Y-%m-%d %H:%M:%S}'), ('Value', '@y')],

    formatters={'x': 'datetime'},)

    

date_range_slider = DateRangeSlider(

    title="Zeitrahmen", start=datesX[0], end=datesX[99],

    value=(datesX[0], datesX[99]), step=1, width=300)


# create a new plot with a title and axis labels

p = figure(

    title='file1', x_axis_label='Date', y_axis_label='yValue',

    y_range=(0, 30), x_axis_type='datetime',

    tools="pan, wheel_zoom, box_zoom, reset",

    plot_width=600, plot_height=200)


# add a line renderer with legend and line thickness

    

p.line(x='x', y='y', source=source, line_width=2)

p.add_tools(hover)


callback = CustomJS(args=dict(source=source, ref_source=source2), code="""

    

    // print out array of date from, date to

    console.log(cb_obj.value); 

    

    // dates returned from slider are not at round intervals and include time;

    const date_from = Date.parse(new Date(cb_obj.value[0]).toDateString());

    const date_to = Date.parse(new Date(cb_obj.value[1]).toDateString());

    

    const data = source.data;

    const ref = ref_source.data;

    

    const from_pos = ref["x"].indexOf(date_from);

    // add + 1 if you want inclusive end date

    const to_pos = ref["x"].indexOf(date_to);

        

    // re-create the source data from "reference"

    data["y"] = ref["y"].slice(from_pos, to_pos);

    data["x"] = ref["x"].slice(from_pos, to_pos);

    

    source.change.emit();

    """)

    

date_range_slider.js_on_change('value', callback)

layout = column(p, date_range_slider)


# show the results

show(layout)


查看完整回答
反对 回复 2024-01-27
?
富国沪深

TA贡献1790条经验 获得超9个赞

我发现上面的答案不起作用,因为数据的时间戳ref_source与来自散景滑块对象(cb_obj)的解析时间戳不同。


例如,ref_source数据中的时间戳在使用 进行解析时会创建以下输出new Date(source.data.["x"]);:


01/01/2020 02:00:00


来自散景滑块对象的时间戳cb_obj始终为00:00:00。因此使用时无法找到时间戳const from_pos = ref["date"].indexOf(date_from);。


为了正确解析日期,ref_source我创建了一个新数组new_ref并将正确解析的日期添加到该数组中。不过,我必须在这里强调,我不是 JavaScript 专家,我很确定在这里可以更有效地编写代码。


这是我的工作示例:


// print out array of date from, date to

console.log(cb_obj.value); 


// dates returned from slider are not at round intervals and include time;

const date_from = Date.parse(new Date(cb_obj.value[0]).toDateString());

const date_to = Date.parse(new Date(cb_obj.value[1]).toDateString());

console.log(date_from, date_to)


// Creating the Data Sources

const data = source.data;

const ref = ref_source.data;


// Creating new Array and appending correctly parsed dates

let new_ref = []

ref["x"].forEach(elem => {

    elem = Date.parse(new Date(elem).toDateString());

    new_ref.push(elem);

    console.log(elem);

})


// Creating Indices with new Array

const from_pos = new_ref.indexOf(date_from);

const to_pos = new_ref.indexOf(date_to) + 1;



// re-create the source data from "reference"

data["y"] = ref["y"].slice(from_pos, to_pos);

data["x"] = ref["x"].slice(from_pos, to_pos);


source.change.emit();

我希望它对你有一点帮助:)


查看完整回答
反对 回复 2024-01-27
?
鸿蒙传说

TA贡献1865条经验 获得超7个赞

有趣的问题和讨论。添加以下两行(其中一行直接从文档中提取)允许滑块在不使用 CustomJS 和 js_on_change 函数的情况下工作 - 使用 js_link 函数代替:

date_range_slider.js_link('value', p.x_range, 'start', attr_selector=0)
date_range_slider.js_link('value', p.x_range, 'end', attr_selector=1)


查看完整回答
反对 回复 2024-01-27
?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

使用 CustonJS 直接更改绘图 x_range 而不是过滤数据源。


callback = CustomJS(args=dict(p=p), code="""

    p.x_range.start = cb_obj.value[0]

    p.x_range.end = cb_obj.value[1]

    p.x_range.change.emit()

    """)


date_range_slider.js_on_change('value_throttled', callback)


查看完整回答
反对 回复 2024-01-27
  • 4 回答
  • 0 关注
  • 118 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信