我正在尝试计算两点的欧式距离。初始距离在数据中计算。
然后,当用户移动线条时,我希望distance
列根据新坐标进行更新。我可以看到x
和y
列正在更新,但是distance
列没有更新。以下是我的尝试:
output_file("tools_point_draw.html")
_tools_to_show = 'box_zoom,pan,save,hover,reset,tap'
p = figure(x_range=(0, 10), y_range=(0, 10), tools=_tools_to_show,
plot_width=862, plot_height=604,
title='Plot name')
p.background_fill_color = 'white'
d_true = {'x': [2, 3], 'y': [4, 1],
'color': ['red', 'red'],
'desc': ['true','true']}
df_true = pd.DataFrame(d_true)
df_true['distance'] = np.sqrt(np.sum((df_true['x'] - df_true['y'])**2))
source = ColumnDataSource(df_true)
renderer2 = p.scatter(x='x', y='y', source=source, color='color', size=15,
line_color='red', line_width=5)
renderer = p.line(x='x', y='y', source=source, color='red',
line_dash='dashed', line_width=10)
columns = [TableColumn(field="x", title="I am X"),
TableColumn(field="y", title="I am Y"),
TableColumn(field='color', title='color'),
TableColumn(field='desc', title='desc'),
TableColumn(field='distance', title='distance')]
update = CustomJS(args=dict(source_data=source), code="""
var data = source_data.data;
var f = cb_obj.value; //is this necessary?
//Sum of squares for euclidean
for(var i = 0, i < data['x'].length; i < size ; i++) {
var res += Math.pow(data['x'][i] - data['y'][i], 2)
}
//Take square root
var res2 = Math.sqrt(res)
//Update table
data['distance'] = res2
source_data.change.emit();
""")
update.js_on_change('tap', update)
table = DataTable(source=source, columns=columns, editable=True, height=200, width=862)
draw_tool = PointDrawTool(renderers=[renderer, renderer2], empty_value='black')
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool
show(Column(p, table))
答案 0 :(得分:2)
您的回调实际上从未在这里触发。这只是pointdraw事件在做事情。 更改source.data时,应具有回调触发器。
source.js_on_change('data', update)
我是从第一个点开始做的,但是你也可以从原点做。
如果是从第一个点开始,则每次都需要更新所有距离(因为绘图工具可让您拖动现有点)
from bokeh.plotting import figure
from bokeh.io import output_file, show
from bokeh.models import DataTable, TableColumn, Column, PointDrawTool, ColumnDataSource, CustomJS
import pandas as pd
import numpy as np
output_file("tools_point_draw.html")
_tools_to_show = 'box_zoom,pan,save,hover,reset,tap'
p = figure(x_range=(0, 10), y_range=(0, 10), tools=_tools_to_show,
plot_width=862, plot_height=604,
title='Plot name')
p.background_fill_color = 'white'
d_true = {'x': [2, 3], 'y': [4, 1],
'color': ['red', 'red'],
'desc': ['true','true']}
df_true = pd.DataFrame(d_true)
df_true['distance'] = [0]+[np.sqrt((df_true['x'][i]-df_true['x'][i-1])**2+(df_true['y'][i]-df_true['y'][i-1])**2) for i in range(1,len(df_true['x']))]
source = ColumnDataSource(df_true)
renderer2 = p.scatter(x='x', y='y', source=source, color='color', size=15,
line_color='red', line_width=5)
renderer = p.line(x='x', y='y', source=source, color='red',
line_dash='dashed', line_width=10)
columns = [TableColumn(field="x", title="I am X"),
TableColumn(field="y", title="I am Y"),
TableColumn(field='color', title='color'),
TableColumn(field='desc', title='desc'),
TableColumn(field='distance', title='distance')]
update = CustomJS(args=dict(source_data=source), code="""
var data = source_data.data;
var res = 0;
//Sum of squares for euclidean
for(var i = 1; i < data['x'].length ; i++) {
res += Math.sqrt(Math.pow(data['x'][i] - data['x'][i-1], 2)+Math.pow(data['y'][i] - data['y'][i-1], 2));
//Update table
data['color'][i] = 'red';
data['desc'][i] = 'true';
data['distance'][i] = res;
}
source_data.change.emit();
""")
source.js_on_change('data', update)
table = DataTable(source=source, columns=columns, editable=True, height=200, width=862)
draw_tool = PointDrawTool(renderers=[renderer, renderer2])
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool
show(Column(p, table))
答案 1 :(得分:0)
虽然找不到方法更新表,但我只是添加了一个Div并更新了其中的文本。不优雅,但是可以完成工作。
div_euclid = Div(text="""
<b>Diameter of predicted form is:</b> 334.80 <br>
<b>Diameter of true form is:</b> 368.64 <br>
<b>RMSE is:</b> 34.13
""",
align='center',
style={'font-size': '100%'})
p.js_on_event(events.MouseMove,
CustomJS(args=dict(div=div_euclid,
source_data_pred=src_pred,
source_data_true=src_true),
code="""
var data_p = source_data_pred.data;
var data_t = source_data_true.data;
var x_p = data_p['x']
var y_p = data_p['y']
var x_t = data_t['x']
var y_t = data_t['y']
var diam_p = 0
var diam_t = 0
var rmse = 0
//Diameter of pred form
diam_p = Math.sqrt(Math.pow((x_p[0]-x_p[1]),2) + Math.pow((y_p[0]-y_p[1]),2))
//Diameter of true form
diam_t = Math.sqrt(Math.pow((x_t[0]-x_t[1]),2) + Math.pow((y_t[0]-y_t[1]),2))
//RMSE
rmse = Math.sqrt(Math.pow(diam_p - diam_t,2)/1)
//Result
div.text = "<b>Diameter of predicted form is: </b>" + diam_p.toFixed(2) + "<br> <b>Diameter of true form is: </b>" + diam_t.toFixed(2) + " <br> <b>RMSE is: </b>" + rmse.toFixed(2);
"""))