是否可以从交互式散景图生成计算出的图/数据?

时间:2019-02-28 23:03:46

标签: python-3.x plot bokeh interactive

我有一个绘图,其中有一些固定的理论点,并且使用多选按钮的组合绘制了实验点。有什么方法可以生成理论点和实验数据之间的%误差数据?

该图的示例如下所示:

Example plot

1 个答案:

答案 0 :(得分:0)

此代码在理论点旁边以文本形式显示百分比误差。您也可以在画布上从理论点到实验点画一条线,差异数据将显示在工具提示中。第一次单击(释放按钮)将启动“差异”行。再次单击将结束它。工具提示始终保留在图上,但是您可以轻松对其进行调整,使其在第二次单击后消失。

import numpy as np
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.events import *

source = ColumnDataSource({'x': [], 'y': []})

p = figure(plot_width = 900)

x_data = np.arange(10)
source_expected = dict(x = x_data, y = np.random.random(10), color=['blue']*10)
source_measured = dict(x = x_data, y = np.random.random(10), color=['grey']*10)

line_expected = p.line('x', 'y', line_color = 'blue', source = source_expected)
circle_expected = p.circle('x', 'y', color = 'color', source = source_expected, size = 10)

line_measured = p.line('x', 'y', line_color = 'grey', source = source_measured)

# circle_measured = p.circle('x', 'y', color = 'color', fill_color = 'white', source = source_measured, size = 60)
circle_measured = p.circle('x', 'y', color = 'color', source = source_measured, size = 10)

source_error = {'x': [], 'y': [], 'error': [], 'percent': [], 'text': [], 'color': []}

i = 0
for expected, measured in zip(source_expected['y'], source_measured['y']):
    source_error['x'].append(x_data[i])
    source_error['y'].append(measured)

    error = expected - measured
    percent = (error/expected) * 100

    source_error['error'].append(error)
    source_error['percent'].append(percent)
    source_error['text'].append('+{}%'.format('%.2f' % percent) if percent > 0 else '{}%'.format('%.2f' % percent))
    source_error['color'].append('green' if expected == measured else 'red')

    i = i + 1

# text_measured = p.text('x', 'y', text = 'text', text_color = 'color', text_font_size = '9pt', x_offset = -25, y_offset = 6, source = source_error)    
text_measured = p.text('x', 'y', text = 'text', text_color = 'color', text_font_size = '9pt', x_offset = 10, y_offset = 5, source = source_error)


line = p.line('x', 'y', line_color = 'red', line_dash = 'dashed', source = source)

callback_tap = '''

if (typeof custom_tooltip == 'undefined') {
        custom_tooltip = document.createElement('div');
        custom_tooltip.setAttribute('id','tooltip_div');
        custom_tooltip.style = 'position: absolute; left: 0px; top: 0px; z-index: 9999; border:1px solid black; padding: 10px; background: white; font-family: arial; font-size: 12px'
        document.body.prepend(custom_tooltip);  
}        

if (true === Bokeh.drawing) {
    Bokeh.drawing = false
}
else {
    if (!Bokeh.drawing) {
        src.data = {'x':[], 'y':[]}        
        src.change.emit()
    }

    src.data['x'].push(cb_obj.x)
    src.data['y'].push(cb_obj.y)

    Bokeh.drawing = true
    Bokeh.sx_start = cb_obj.sx
    Bokeh.x_start = cb_obj.x
    Bokeh.sy_start = cb_obj.sy
    Bokeh.y_start = cb_obj.y  
}'''

callback_mousemove = '''
function print(...args) {
    for (i in args) {
        console.log(args[i])
    }
}

if (Bokeh.drawing) {  
    if (src.data['x'].length > 1) {
        src.data['x'].pop()
        src.data['y'].pop()   
    }

    src.data['x'].push(cb_obj.x)
    src.data['y'].push(cb_obj.y)
    src.change.emit()

    tooltip = document.getElementById('tooltip_div')
    tooltip.style.left = cb_obj.sx + 30 + 'px'
    tooltip.style.top = cb_obj.sy + 10 + 'px'
    var error = Math.round((Bokeh.y_start - cb_obj.y) * 100) / 100
    var percent = Math.round(error * 100 / Bokeh.y_start) 
    tooltip.innerHTML = 'Distance X: ' + Math.round(Bokeh.sx_start - cb_obj.sx) + ' px' + ' (' + (Math.round((Bokeh.x_start - cb_obj.x) * 100) / 100) + ' units)' + 
                        '<br />' +
                        'Distance Y: ' + Math.round(Bokeh.sy_start - cb_obj.sy) + ' px' + ' (' + (Math.round((Bokeh.y_start - cb_obj.y) * 100) / 100) + ' units)' + 
                        '<br />' +
                        'Error: ' + error + ' units' + 
                        '<br />' +
                        '% Error: ' + percent + ' %'
}'''

p.js_on_event('tap', CustomJS(args = {'src': source, }, code = callback_tap))
p.js_on_event('mousemove', CustomJS(args = {'src': source, }, code = callback_mousemove))

show(p)

结果:

enter image description here

将注释的行与其旁边的行交换会使错误百分比显示在代表理论点的大圆圈中。看起来像这样:

enter image description here

此脚本是为 Python v3.7.2 Bokeh v1.3.0

编写的