Have Bokeh Callback update a list instead of a ColumnDataSource?

ⅰ亾dé卋堺 提交于 2019-12-04 14:48:12

Let me know if I'm misunderstanding your question, but it sounds like you have a matrix (let's call it m1), that you're running a transform on and yielding the data in s1 and plotting in p1. You want be able to select some of the data in p1 and have the corresponding data in m1 be plotted in p2.

If that's right, you're going to need three ColumnDataSource objects. You're going to have to create a ColumnDataSource for m1 and use the s1 and s2 you already have. The Callback will need to be something like:

s1.callback = Callback(args=dict(m1=m1, s2=s2), code="""
  var inds = cb_obj.get('selected')['1d'].indices;
  var d1 = m1.get('data');
  var d2 = s2.get('data');
  d2['x'] = []
  d2['y'] = []
  for (i = 0; i < inds.length; i++) {
    d2['x'].push(d1['x'][inds[i]])
    d2['y'].push([inds[i]])
  }
  s2.trigger('change'); 
""")

This will take the indices of the selected data and find the x and y with the same indices m1 (the raw data) and add it to s2. Then the s2.trigger('change') call will update the data points in p2.

Let me know if I'm misunderstanding your question.

Not having any responses neither here nor in Bokeh mailing list, leads me to believe it's impossible to use callback in this manner, so I had to work around it and accept the current limitation, which is the inability to make the callback "size" dynamic.

For the purpose of data exploration this will do anyway.

from bokeh.plotting import figure, output_file, show, ColumnDataSource, hplot
from bokeh.models import HoverTool, Callback, ColumnDataSource
import pandas as pd
output_file("bla.html")

# Mock data
m = np.ones((6,11))
for i in range(2,6):
    for j in range(11):
        m[i,j] = i+j
x = [0,1,2,3,4,5]; y = [0,2,4,6,8,10]
m0 = m.transpose()
m1 = pd.DataFrame(m0, index=['0','1','2','3','4','5','6','7','8','9','10'], columns=[np.arange(0,len(m),1).astype(str)])

#First plot
s1 = ColumnDataSource(data=dict(x=x,y=y))
p1 = figure(tools=["lasso_select"], plot_width=600, plot_height=400)
p1.scatter('x', 'y', fill_color='black', line_color=None, size=10, source=s1)

#Second plot
s2 = ColumnDataSource(data=dict(x=[],y=[],y2=[]))
p2 = figure(plot_width=400, plot_height=400, tools =[])

m1 = ColumnDataSource(m1) #Actual Datasource for the second plot
p2.line(np.arange(0,100,1), 'y' , source=s2) # From original data - series 1
p2.line(np.arange(0,100,1), 'y2' , source=s2) # From original data - series 2

s1.callback = Callback(args=dict(s2=s2, m1=m1), code="""
  var inds = cb_obj.get('selected')['1d'].indices;
  var d1 = m1.get('data'); 
  var d2 = s2.get('data');
  d2['y'] = []
  d2['y2'] = []
  for (i = 0; i < 11; i++) {
    d2['y'].push(d1[inds['0']][i]),
    d2['y2'].push(d1[inds['1']][i])
  }
  s2.trigger('change'); 
""")

layout = hplot(p1, p2)
show(layout)

The code now plots the original data for the first two indices in the selected data. The limitation is that the number of series of original data has to be predefined, so the code will only ever plot two lines. Furthermore it also needs at least two lines to execute. It won't work for only one point selected. Therefore if I predefine more lines to be graphed I will always have to select that number of points.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!