我正在创建一个基于其特定属性显示一系列产品的图表。我模仿了Bokeh的“电影”例子中的图形设计。我可以通过导航到源文件夹并执行"bokeh serve --show shafts" from Andaconda Prompt
来运行我的绘图。
我的问题是我需要保存一个HTML文件,这样我就可以将它分发给多个人,而不需要附带数据库。如果我尝试保存HTML文件
"output_file("slider.html", title="slider.py example")"
来自电影示例或我的代码,然后从HTML文件中运行图表,滑块不会更新图表。我认为问题在于,当您从"bokeh serve --show shafts" from Andaconda Prompt
运行文件时,它正在服务器上运行,并且能够连续访问python代码。
或者,当您从HTML运行它时,它将所有代码都符合JASON格式,并且无法再访问python代码。为了解决这个问题,Bokeh添加了一些小的Javascript部分,这些部分将继续更新服务器。
Bokeh提供了如何执行此操作的多个示例,但我没有完全掌握在Javascript中更新的内容以更新图表。我对JS并不是非常熟悉,所以它让它变得有点困难。他们给出的一个简单例子是:
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure, output_file, show
output_file("callback.html")
x = [j*0.005 for j in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
callback = CustomJS(args=dict(source=source), code="""
var data = source.data;
var f = cb_obj.value
x = data['x']
y = data['y']
for (i = 0; i < x.length; i++) {
y[i] = Math.pow(x[i], f)
}
source.change.emit();
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=callback)
layout = column(slider, plot)
show(layout)
y[i]
显然正在更新以更新绘图,但我无法弄清楚它与原始python更新的关系,或者为什么此更改可能会影响看似超出其范围的图形。我真正的问题是在我的代码中,我需要更新哪些变量才能使代码在Bokeh服务器上运行。
贝娄是我的代码。 for control in controls: control.on_change('value'.......
是在服务器上更新图形的代码部分,但我需要将其替换为JavaScript代码,以便在保存为HTML时更新。
callback = CustomJS(args=dict(source=source), code="""
var data = source.data;
selected = shafts[
((data.Weight2g >= min_weight_slider.value) &&
(data.Weight2g <= max_weight_slider.value) &&
(data.Butt_Frequency >= min_butt_freq_slider.value) &&
(data.Butt_Frequency <= max_butt_freq_slider.value) &&
(data.Tip_Frequency >= min_tip_freq_slider.value) &&
(data.Tip_Frequency <= max_tip_freq_slider.value) &&
(data.Torque >= min_torque_slider.value) &&
(data.Torque <= max_torque_slider.value))
];
data = selected;
source.data = selected;
}
source.change.emit;
""")
min_weight_slider = Slider(title="Minimum Weight", value=40,
start=40.0, end=200.0, step=0.5, callback = callback)
callback.args["min_weight_slider"] = min_weight_slider
max_weight_slider = Slider(title="Maximum Weight", value=200, start=40.0, end=200.0, step=0.5, callback = callback)
callback.args["max_weight_slider"] = max_weight_slider
min_butt_freq_slider = Slider(title="Minimum Butt Frequency", value=180.0, start=100.0, end=500.0, step=10.0, callback = callback)
callback.args["min_butt_freq_slider"] = min_butt_freq_slider
max_butt_freq_slider = Slider(title="Maximum Butt Frequency", value=500.0, start=100.0, end=500.0, step=10.0, callback = callback)
callback.args["max_butt_freq_slider"] = max_butt_freq_slider
min_tip_freq_slider = Slider(title="Minimum Tip Frequency", value=180, start=100, end=500, step=10, callback = callback)
callback.args["min_tip_freq_slider"] = min_tip_freq_slider
max_tip_freq_slider = Slider(title="Maximum Tip Frequency", value=400, start=100, end=500, step=10, callback = callback)
callback.args["max_tip_freq_slider"] = max_tip_freq_slider
min_torque_slider = Slider(title="Minimum Torque", value=2, start=1, end=20, step=0.1, callback = callback)
callback.args["min_torque_slider"] = min_torque_slider
max_torque_slider = Slider(title="Maximum Torque", value=15, start=1, end=20, step=0.1, callback = callback)
callback.args["max_torque_slider"] = max_torque_slider
x_axis = Select(title="X Axis", options=sorted(axis_map.keys()), value="Butt_Frequency")
callback.args["x_axis"] = x_axis
y_axis = Select(title="Y Axis", options=sorted(axis_map.keys()), value="Tip_Frequency")
callback.args["y_axis"] = y_axis
def select_shafts():
selected = shafts[
(shafts.Weight2g >= min_weight_slider.value) &
(shafts.Weight2g <= max_weight_slider.value) &
(shafts.Butt_Frequency >= min_butt_freq_slider.value) &
(shafts.Butt_Frequency <= max_butt_freq_slider.value) &
(shafts.Tip_Frequency >= min_tip_freq_slider.value) &
(shafts.Tip_Frequency <= max_tip_freq_slider.value) &
(shafts.Torque >= min_torque_slider.value) &
(shafts.Torque <= max_torque_slider.value)
]
return selected
#updates the
def update():
df = select_shafts()
#re-names the above function
x_name = axis_map[x_axis.value]
y_name = axis_map[y_axis.value]
p.xaxis.axis_label = x_axis.value
p.yaxis.axis_label = y_axis.value
p.title.text = "%d shafts selected" % len(df)
source.data = dict(
x=df[x_name],
y=df[y_name],
color=df["color"],
Manufacture=df["Manufacture"],
Model = df["Model"],
Type = df["Type"],
Weight = df["Weight"],
Flex=df["Flex"],
Butt_Frequency = df["Butt_Frequency"],
Tip_Frequency = df["Tip_Frequency"],
Torque=df["Torque"],
Weight2G = df["Weight2g"],
Availability = df["Availability"],
alpha=df["alpha"]
)
controls = [min_weight_slider, max_weight_slider, min_butt_freq_slider, max_butt_freq_slider, min_tip_freq_slider, max_tip_freq_slider, min_torque_slider, max_torque_slider,x_axis, y_axis]
#for control in controls:
#control.on_change('value', lambda attr, old, new: update())
sizing_mode = 'fixed' # 'scale_width' also looks nice with this example
inputs = widgetbox(*controls, sizing_mode=sizing_mode)
#Widget box produced with bokeh
l = layout([
[inputs, p]
], sizing_mode=sizing_mode)
update() # initial load of the data
curdoc().add_root(l)
curdoc().title = "Shafts"
show(l)
非常感谢
我想在我尝试解决问题时添加更新。我意识到当程序在Bokeh服务器上运行时,它能够不断更新绘图功能可以访问的源数据。当程序运行JS函数时,它只能更新单个字典条目键中的值。
我正试图modify这段代码来模仿我的需要。
答案 0 :(得分:2)
为了获得我想要的结果,我通过javascript回调传递了两个数据集。从未修改的原始数据,然后是根据最终更新绘图的选定条件修改的原始数据的副本。
callback = CustomJS(args={"orgData": originalData, "modData": sourceData}, code="""
var oData = orgData.data;
var updateData = modData.data;
var holdData = {'x':[],'y':[],'color':[], 'Manufacture':[],'Model':[],'Type':[],
'Weight':[],'Flex':[],'Butt_Frequency':[],'Tip_Frequency':[],
'Torque':[],'WeightMes':[],'Availability':[],'alpha':[]};
console.log(Manufacture_Select.value.includes(oData.Manufacture[1]));
//console.log(min_weight_slider.value);
//console.log((oData.WeightMes[1] >= min_weight_slider.value) && (oData.WeightMes[1] <= max_weight_slider.value));
var xAxisSelection = String(x_axis.value);
var yAxisSelection = String(y_axis.value);
var avalButSelNames = [];
for (i = 0; i < avalibility_Button.active.length; i++){
avalButSelNames.push(avalibility_Button.labels[avalibility_Button.active[i]]);
}
console.log(avalButSelNames)
for(i = 0; i < oData.Manufacture.length; i++){
if((oData.WeightMes[i] >= weight_slider.value[0])&&
(oData.WeightMes[i] <= weight_slider.value[1]) &&
(oData.Butt_Frequency[i] >= butt_freq_slider.value[0]) &&
(oData.Butt_Frequency[i] <= butt_freq_slider.value[1]) &&
(oData.Tip_Frequency[i] >= tip_freq_slider.value[0]) &&
(oData.Tip_Frequency[i] <= tip_freq_slider.value[1]) &&
(oData.Torque[i] >= torque_slider.value[0]) &&
(oData.Torque[i] <= torque_slider.value[1]) &&
(oData.Balance_Point[i] <= Balance_Point_Slider.value[1]) &&
(oData.Balance_Point[i] >= Balance_Point_Slider.value[0]) &&
(Manufacture_Select.value.includes(oData.Manufacture[i])) &&
(Type_Select.value.includes(oData.Type[i]))&&
(Flex_Select.value.includes(oData.Flex[i]))&&
(avalButSelNames.includes(oData.Availability[i]))
){
holdData['x'].push(oData[xAxisSelection][i]);
holdData['y'].push(oData[yAxisSelection][i]);
holdData['color'].push(oData.color[i]);
holdData['Manufacture'].push(oData.Manufacture[i]);
holdData['Model'].push(oData.Model[i]);
holdData['Type'].push(oData.Type[i]);
holdData['Weight'].push(oData.Weight[i]);
holdData['Flex'].push(oData.Flex[i]);
holdData['Butt_Frequency'].push(oData.Butt_Frequency[i]);
holdData['Tip_Frequency'].push(oData.Tip_Frequency[i]);
holdData['Torque'].push(oData.Torque[i]);
holdData['WeightMes'].push(oData.WeightMes[i]);
holdData['Availability'].push(oData.Availability[i]);
holdData['alpha'].push(oData.alpha[i]);
//console.log(i);
}
}
modData.data = holdData;
labels = plot.get('renderers');
labels[0]["attributes"]["axis_label"] = xAxisSelection;
labels[2]["attributes"]["axis_label"] = yAxisSelection;
""")