如何在 Bokeh (Python) 中编写自定义 JS 回调?
How to write custom JS callback in Bokeh (Python)?
我尝试用 Bokeh 构建一个动态图表,但我被 JavaScript 部分卡住了,用自定义 JS 回调的措辞。我明确表示我绝对不熟悉 JavaScript.
这是我的数据框:
num_tra num_ts Item annee valeur TRA_label TS_label TRA TS Sensi Cumul
1 1 PVFP 10 62 0 bps 0 bps 0 0 Sensi 62
1 1 PVFP 20 28 0 bps 0 bps 0 0 Sensi 90
1 1 PVFP 30 87 0 bps 0 bps 0 0 Sensi 177
1 2 PVFP 10 25 0 bps - 15 bps 0 -15 Sensi 25
1 2 PVFP 20 95 0 bps - 15 bps 0 -15 Sensi 120
1 2 PVFP 30 95 0 bps - 15 bps 0 -15 Sensi 215
2 1 PVFP 10 49 - 10 bps 0 bps -10 0 Sensi 49
2 1 PVFP 20 17 - 10 bps 0 bps -10 0 Sensi 66
2 1 PVFP 30 98 - 10 bps 0 bps -10 0 Sensi 164
2 2 PVFP 10 83 - 10 bps - 15 bps -10 -15 Sensi 83
2 2 PVFP 20 58 - 10 bps - 15 bps -10 -15 Sensi 141
2 2 PVFP 30 52 - 10 bps - 15 bps -10 -15 Sensi 193
1 1 PVFP 10 44 0 bps 0 bps 0 0 Central 44
1 1 PVFP 20 60 0 bps 0 bps 0 0 Central 104
1 1 PVFP 30 97 0 bps 0 bps 0 0 Central 201
1 2 PVFP 10 82 0 bps - 15 bps 0 -15 Central 82
1 2 PVFP 20 88 0 bps - 15 bps 0 -15 Central 170
1 2 PVFP 30 38 0 bps - 15 bps 0 -15 Central 208
2 1 PVFP 10 58 - 10 bps 0 bps -10 0 Central 58
2 1 PVFP 20 30 - 10 bps 0 bps -10 0 Central 88
2 1 PVFP 30 69 - 10 bps 0 bps -10 0 Central 157
2 2 PVFP 10 2 - 10 bps - 15 bps -10 -15 Central 2
2 2 PVFP 20 62 - 10 bps - 15 bps -10 -15 Central 64
2 2 PVFP 30 69 - 10 bps - 15 bps -10 -15 Central 133
我正在寻找的是一个图表,其中包含两个滑块(slider_TRA 和 slider_TS),基于变量 num_tra 和 num_ts 的值。最后,我想根据两个滑块的值更新绘图的来源。
基于 Bokeh 文档示例,我尝试构建以下代码,但不知道如何处理 JS 部分:
import numpy as np
import pandas as pd
from bokeh.models import ColumnDataSource, CustomJS, Slider
from bokeh.layouts import column, row
from bokeh.plotting import ColumnDataSource, figure, output_file, show
central=pvfp.loc[pvfp.Sensi=="Central"]
sensi=pvfp.loc[pvfp.Sensi=="Sensi"]
source1 = ColumnDataSource(central)
source2 = ColumnDataSource(sensi)
plot = figure(plot_width=400, plot_height=400)
plot.line('annee', 'valeur', source=source1)
plot.line('annee', 'valeur', source=source2)
slider_TRA = Slider(start=1, end=2, value=1, step=1, title="Sensi TRA")
slider_TS = Slider(start=1, end=2, value=1, step=1, title="Sensi TS")
callback = CustomJS(
args=dict(source1=source1,source2=source2, slider_TRA=slider_TRA,slider_TS=slider_TS),
code="""
const data1 = source1.data;
const data2 = source2.data;
const stra = slider_TRA.value;
const sts = slider_TS.value;
const num_tra1 = data1['num_tra']
const num_ts1 = data1['num_ts']
const num_tra2 = data2['num_tra']
const num_ts2 = data2['num_ts']
for ...some JS to say :
num_tra1=num_tra2=stra
num_ts1=num_ts2=sts
and
source1=source1.loc[(source1.num_tra==num_tra1)&(source1.num_ts==num_ts1)]
source2=source2.loc[(source2.num_tra==num_tra2)&(source2.num_ts==num_ts2)]
source1.change.emit();
source2.change.emit();
"""
)
slider_TRA.js_on_change('value', callback)
slider_TS.js_on_change('value',callback)
layout = row(
plot,
column(slider_TRA, slider_TS),
)
show(layout)
如上所述,我不熟悉 JS,我正在寻找可以帮助我的人。
如果您有任何想法或建议,我们将不胜感激。
此解决方案适用于 Bokeh v2.3.0。
您需要将完整的数据传递给回调函数,并根据滑块值进行过滤。但是您不能将生成的过滤数据分配给原始数据,因为这样会丢失信息。因此,您应该将过滤后的数据分配给相应字形的 data_source
对象。
此外,两条线的起始数据应根据初始滑块位置进行过滤。
import os
import pandas as pd
from bokeh.models import ColumnDataSource, CustomJS, Slider, BooleanFilter, CDSView
from bokeh.layouts import column, row
from bokeh.plotting import figure, show
pvfp = pd.read_csv(os.path.join(os.path.dirname(__file__), 'data', 'data.csv'), sep = ",")
central = pvfp.loc[pvfp.Sensi=="Central"]
sensi = pvfp.loc[pvfp.Sensi=="Sensi"]
min_tra_central = central['num_tra'].min()
max_tra_central = central['num_tra'].max()
min_ts_central = central['num_ts'].min()
max_ts_central = central['num_ts'].max()
min_tra_sensi = sensi['num_tra'].min()
max_tra_sensi = sensi['num_tra'].max()
min_ts_sensi = sensi['num_ts'].min()
max_ts_sensi = sensi['num_ts'].max()
start_tra = min(min_tra_central, min_tra_sensi)
start_ts = min(min_ts_central, min_ts_sensi)
end_tra = max(max_tra_central, max_tra_sensi)
end_ts = max(max_ts_central, max_ts_sensi)
slider_TRA = Slider(start = start_tra, end = end_tra, value=start_tra, step=1, title="Sensi TRA", show_value = False)
slider_TS = Slider(start = start_ts, end = end_ts, value=start_ts, step=1, title="Sensi TS", show_value = False)
##########################################################################
plot = figure(plot_width=400, plot_height=400)
source_central = ColumnDataSource(central)
source_sensi = ColumnDataSource(sensi)
source_start_central = central.loc[(central['num_tra'] == start_tra) & (central['num_ts'] == start_ts)]
source_start_sensi = sensi.loc[(sensi['num_tra'] == start_tra) & (sensi['num_ts'] == start_ts)]
line_central = plot.line('annee', 'valeur', color = 'red', source=source_start_central)
line_sensi = plot.line('annee', 'valeur', color = 'blue', source=source_start_sensi)
##########################################################################
callback = CustomJS(
args=dict(source_central=source_central, source_sensi=source_sensi, line_central=line_central, line_sensi=line_sensi, slider_TRA=slider_TRA, slider_TS=slider_TS),
code="""
const data_central = source_central.data;
const data_sensi = source_sensi.data;
const tra_value = slider_TRA.value;
const ts_value = slider_TS.value;
var new_central_y = []
var new_sensi_y = []
for (var i=0; i<data_central['num_tra'].length; i++) {
if(data_central['num_tra'][i] == tra_value && data_central['num_ts'][i] == ts_value) {
new_central_y.push(data_central['valeur'][i])
}
if(data_sensi['num_tra'][i] == tra_value && data_sensi['num_ts'][i] == ts_value) {
new_sensi_y.push(data_sensi['valeur'][i])
}
}
line_central.data_source.data['valeur'] = new_central_y
line_sensi.data_source.data['valeur'] = new_sensi_y
line_central.data_source.change.emit();
line_sensi.data_source.change.emit();
"""
)
slider_TRA.js_on_change('value', callback)
slider_TS.js_on_change('value', callback)
layout = row(
plot,
column(slider_TRA, slider_TS),
)
show(layout)
结果:
我尝试用 Bokeh 构建一个动态图表,但我被 JavaScript 部分卡住了,用自定义 JS 回调的措辞。我明确表示我绝对不熟悉 JavaScript.
这是我的数据框:
num_tra num_ts Item annee valeur TRA_label TS_label TRA TS Sensi Cumul
1 1 PVFP 10 62 0 bps 0 bps 0 0 Sensi 62
1 1 PVFP 20 28 0 bps 0 bps 0 0 Sensi 90
1 1 PVFP 30 87 0 bps 0 bps 0 0 Sensi 177
1 2 PVFP 10 25 0 bps - 15 bps 0 -15 Sensi 25
1 2 PVFP 20 95 0 bps - 15 bps 0 -15 Sensi 120
1 2 PVFP 30 95 0 bps - 15 bps 0 -15 Sensi 215
2 1 PVFP 10 49 - 10 bps 0 bps -10 0 Sensi 49
2 1 PVFP 20 17 - 10 bps 0 bps -10 0 Sensi 66
2 1 PVFP 30 98 - 10 bps 0 bps -10 0 Sensi 164
2 2 PVFP 10 83 - 10 bps - 15 bps -10 -15 Sensi 83
2 2 PVFP 20 58 - 10 bps - 15 bps -10 -15 Sensi 141
2 2 PVFP 30 52 - 10 bps - 15 bps -10 -15 Sensi 193
1 1 PVFP 10 44 0 bps 0 bps 0 0 Central 44
1 1 PVFP 20 60 0 bps 0 bps 0 0 Central 104
1 1 PVFP 30 97 0 bps 0 bps 0 0 Central 201
1 2 PVFP 10 82 0 bps - 15 bps 0 -15 Central 82
1 2 PVFP 20 88 0 bps - 15 bps 0 -15 Central 170
1 2 PVFP 30 38 0 bps - 15 bps 0 -15 Central 208
2 1 PVFP 10 58 - 10 bps 0 bps -10 0 Central 58
2 1 PVFP 20 30 - 10 bps 0 bps -10 0 Central 88
2 1 PVFP 30 69 - 10 bps 0 bps -10 0 Central 157
2 2 PVFP 10 2 - 10 bps - 15 bps -10 -15 Central 2
2 2 PVFP 20 62 - 10 bps - 15 bps -10 -15 Central 64
2 2 PVFP 30 69 - 10 bps - 15 bps -10 -15 Central 133
我正在寻找的是一个图表,其中包含两个滑块(slider_TRA 和 slider_TS),基于变量 num_tra 和 num_ts 的值。最后,我想根据两个滑块的值更新绘图的来源。
基于 Bokeh 文档示例,我尝试构建以下代码,但不知道如何处理 JS 部分:
import numpy as np
import pandas as pd
from bokeh.models import ColumnDataSource, CustomJS, Slider
from bokeh.layouts import column, row
from bokeh.plotting import ColumnDataSource, figure, output_file, show
central=pvfp.loc[pvfp.Sensi=="Central"]
sensi=pvfp.loc[pvfp.Sensi=="Sensi"]
source1 = ColumnDataSource(central)
source2 = ColumnDataSource(sensi)
plot = figure(plot_width=400, plot_height=400)
plot.line('annee', 'valeur', source=source1)
plot.line('annee', 'valeur', source=source2)
slider_TRA = Slider(start=1, end=2, value=1, step=1, title="Sensi TRA")
slider_TS = Slider(start=1, end=2, value=1, step=1, title="Sensi TS")
callback = CustomJS(
args=dict(source1=source1,source2=source2, slider_TRA=slider_TRA,slider_TS=slider_TS),
code="""
const data1 = source1.data;
const data2 = source2.data;
const stra = slider_TRA.value;
const sts = slider_TS.value;
const num_tra1 = data1['num_tra']
const num_ts1 = data1['num_ts']
const num_tra2 = data2['num_tra']
const num_ts2 = data2['num_ts']
for ...some JS to say :
num_tra1=num_tra2=stra
num_ts1=num_ts2=sts
and
source1=source1.loc[(source1.num_tra==num_tra1)&(source1.num_ts==num_ts1)]
source2=source2.loc[(source2.num_tra==num_tra2)&(source2.num_ts==num_ts2)]
source1.change.emit();
source2.change.emit();
"""
)
slider_TRA.js_on_change('value', callback)
slider_TS.js_on_change('value',callback)
layout = row(
plot,
column(slider_TRA, slider_TS),
)
show(layout)
如上所述,我不熟悉 JS,我正在寻找可以帮助我的人。 如果您有任何想法或建议,我们将不胜感激。
此解决方案适用于 Bokeh v2.3.0。
您需要将完整的数据传递给回调函数,并根据滑块值进行过滤。但是您不能将生成的过滤数据分配给原始数据,因为这样会丢失信息。因此,您应该将过滤后的数据分配给相应字形的 data_source
对象。
此外,两条线的起始数据应根据初始滑块位置进行过滤。
import os
import pandas as pd
from bokeh.models import ColumnDataSource, CustomJS, Slider, BooleanFilter, CDSView
from bokeh.layouts import column, row
from bokeh.plotting import figure, show
pvfp = pd.read_csv(os.path.join(os.path.dirname(__file__), 'data', 'data.csv'), sep = ",")
central = pvfp.loc[pvfp.Sensi=="Central"]
sensi = pvfp.loc[pvfp.Sensi=="Sensi"]
min_tra_central = central['num_tra'].min()
max_tra_central = central['num_tra'].max()
min_ts_central = central['num_ts'].min()
max_ts_central = central['num_ts'].max()
min_tra_sensi = sensi['num_tra'].min()
max_tra_sensi = sensi['num_tra'].max()
min_ts_sensi = sensi['num_ts'].min()
max_ts_sensi = sensi['num_ts'].max()
start_tra = min(min_tra_central, min_tra_sensi)
start_ts = min(min_ts_central, min_ts_sensi)
end_tra = max(max_tra_central, max_tra_sensi)
end_ts = max(max_ts_central, max_ts_sensi)
slider_TRA = Slider(start = start_tra, end = end_tra, value=start_tra, step=1, title="Sensi TRA", show_value = False)
slider_TS = Slider(start = start_ts, end = end_ts, value=start_ts, step=1, title="Sensi TS", show_value = False)
##########################################################################
plot = figure(plot_width=400, plot_height=400)
source_central = ColumnDataSource(central)
source_sensi = ColumnDataSource(sensi)
source_start_central = central.loc[(central['num_tra'] == start_tra) & (central['num_ts'] == start_ts)]
source_start_sensi = sensi.loc[(sensi['num_tra'] == start_tra) & (sensi['num_ts'] == start_ts)]
line_central = plot.line('annee', 'valeur', color = 'red', source=source_start_central)
line_sensi = plot.line('annee', 'valeur', color = 'blue', source=source_start_sensi)
##########################################################################
callback = CustomJS(
args=dict(source_central=source_central, source_sensi=source_sensi, line_central=line_central, line_sensi=line_sensi, slider_TRA=slider_TRA, slider_TS=slider_TS),
code="""
const data_central = source_central.data;
const data_sensi = source_sensi.data;
const tra_value = slider_TRA.value;
const ts_value = slider_TS.value;
var new_central_y = []
var new_sensi_y = []
for (var i=0; i<data_central['num_tra'].length; i++) {
if(data_central['num_tra'][i] == tra_value && data_central['num_ts'][i] == ts_value) {
new_central_y.push(data_central['valeur'][i])
}
if(data_sensi['num_tra'][i] == tra_value && data_sensi['num_ts'][i] == ts_value) {
new_sensi_y.push(data_sensi['valeur'][i])
}
}
line_central.data_source.data['valeur'] = new_central_y
line_sensi.data_source.data['valeur'] = new_sensi_y
line_central.data_source.change.emit();
line_sensi.data_source.change.emit();
"""
)
slider_TRA.js_on_change('value', callback)
slider_TS.js_on_change('value', callback)
layout = row(
plot,
column(slider_TRA, slider_TS),
)
show(layout)
结果: