使用Python中的Bokeh将图之间的跨度或光标链接

问题描述:

我想要一个链接到Bokeh中各图之间的光标.因此,如果将光标移动到一个图上,则在相邻图上会显示一条等效线.我还没有弄清楚如何使用内置的光标工具.因此,我当前的解决方案是在共享源的每个图上画一条线.然后,当我将鼠标悬停在任一图上时,将更新源.

I would like a cursor that is linked between to plots in Bokeh. So if I move my cursor on one plot, an equivalent line shows up on an adjacent plot. I haven't figured out how to do it with the built in cursor tool. So my current solution is to draw a line on each plot that shares a source. Then when I hover over either plot, the source is updated.

此方法有2个问题:1.似乎是一种解决方法2.当前的线是有限长度的.我希望这条线是无限的,所以无论图形大小如何调整,该线都会越过边缘.目前,我画的线是有限的.绘制无限水平线的正确方法是Span注释,但是我很难弄清楚如何通过回调传递/更新Span位置.请参阅下面的代码.

I have 2 issues with this method: 1. It seems like a workaround 2. Currently the lines are finite length. I would like the line to be infinite, so no matter how the graph is resized, the line runs off the edge. Currently the line I draw is finite. The right way to draw an infinite horizontal line is a Span annotation, but I am having a hard time figuring out how to pass/update the Span location through my callback. See my code below.

from bokeh.io import gridplot, show, output_notebook, output_file
from bokeh.plotting import figure
from bokeh.models import HoverTool, ColumnDataSource, CustomJS, Span


output_notebook()

x = list(range(11))
y1 = x
y2 = [10 - i for i in x]


source = ColumnDataSource({'x0': [0], 'y0': [2], 'x1': [10], 'y1': [2]})

# create a new plot
s1 = figure(width=250, plot_height=250, tools="", title=None)
cr1 = s1.circle(x, y1, size=10, color="navy", alpha=0.5)
sr1 = s1.segment(x0='x0', y0='y0', x1='x1', y1='y1', color='red', alpha=1, line_width=1, source=source, )
sp1 = Span(location=source.data['y0'][0], dimension='width', line_color='green') 
s1.renderers.extend([sp1,])

# create another one
s2 = figure(width=250, height=250, title=None)
cr2 = s2.triangle(x, y1, size=10, color="firebrick", alpha=0.5)
sr2 = s2.segment(x0='x0', y0='y0', x1='x1', y1='y1', color='red', alpha=1, line_width=1, source=source, )

# put all the plots in an HBox
p = gridplot([[s1,s2],[]])

code = """
var data = {'x0': [], 'y0': [], 'x1': [], 'y1': []};
var cdata = circle.get('data');
var indices = cb_data.index['1d'].indices;

for (i=0; i < indices.length; i++) {
    ind0 = indices[i];
        data['x0'].push(0);
        data['y0'].push(cdata.y[ind0]);
        data['x1'].push(10);
        data['y1'].push(cdata.y[ind0]);

}
segment.set('data', data);
""" 


callback1 = CustomJS(args={'circle': cr1.data_source, 'segment': sr2.data_source}, code=code)
s1.add_tools(HoverTool(tooltips=None, callback=callback1, renderers=[cr1]))

callback2 = CustomJS(args={'circle': cr2.data_source, 'segment': sr2.data_source}, code=code)
s2.add_tools(HoverTool(tooltips=None, callback=callback2, renderers=[cr2]))


# show the results
show(p)

感谢@bigreddot提供了有关跨度的答案.我曾经尝试过但没有参加工作,但通过他的提示就弄清楚了.工作代码如下.我在每个图上实现了一个跨度,然后编辑每个图的位置.

Thanks to @bigreddot for his answer on the spans. I had tried and not gotten in to work, but figured it out with his hints. The working code is below. I implemented a span in each plot and then edit the location of each.

from bokeh.io import gridplot, show, output_notebook, output_file
from bokeh.plotting import figure
from bokeh.models import HoverTool, ColumnDataSource, CustomJS, Span

output_file('Test.html')

#output_notebook()

x = list(range(11))
y1 = x
y2 = [10 - i for i in x]

# create a new plot
s1 = figure(width=250, plot_height=250, tools="", title=None)
cr1 = s1.circle(x, y1, size=10, color="navy", alpha=0.5)
sp1 = Span(location=source.data['y0'][0], dimension='width', line_color='green',  render_mode='css') 
s1.renderers.extend([sp1,])

# create another one
s2 = figure(width=250, height=250, title=None)
cr2 = s2.triangle(x, y1, size=10, color="firebrick", alpha=0.5)
sp2 = Span(location=source.data['y0'][0], dimension='width', line_color='green', render_mode='css')
s2.renderers.extend([sp2,])

# put all the plots in an HBox
p = gridplot([[s1,s2],[]])

code = """
var cdata = circle.get('data');
var indices = cb_data.index['1d'].indices;

var sum = 0;

for (i=0; i < indices.length; i++) {
    sum += cdata.y[indices[i]];
}

var avg = sum/indices.length
span1.set('location', [avg])
span2.set('location', [avg])
""" 

callback1 = CustomJS(args={'circle': cr1.data_source,  'span1': sp1, 'span2': sp2}, code=code)
s1.add_tools(HoverTool(tooltips=None, callback=callback1, renderers=[cr1]))

callback2 = CustomJS(args={'circle': cr2.data_source, 'span1': sp1, 'span2': sp2}, code=code)
s2.add_tools(HoverTool(tooltips=None, callback=callback2, renderers=[cr2]))

# show the results
show(p)