Обновление DataTable на событие Tap в Боке - PullRequest
0 голосов
/ 25 июня 2019

Я пытаюсь вычислить евклидово расстояние двух точек.Начальное расстояние рассчитывается в данных.

Затем, когда пользователь перемещает линию, я бы хотел, чтобы столбец 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))

Ответы [ 2 ]

2 голосов
/ 30 июня 2019

Ваш обратный вызов здесь на самом деле никогда не срабатывает. Это просто событие 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))
0 голосов
/ 01 июля 2019

Хотя я не мог найти способ обновить таблицу, я просто добавил 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);

     """))
...