Have the Bokeh Callback update the list instead of the ColumnDataSource?

My question is related to modifying this example from the Bokeh gallery.

I have a matrix m or raw data where each row corresponds to a pair of x, y coordinates in the transformed space (see some of the code in MockData).

The x, y coordinates are plotted on the left graph. I am trying to modify the example so that when I select multiple points in the left figure, the right figure will display the row plots of the corresponding rows.

I've narrowed the problem down to the point where the right figure will show the data it wants, as indicated in the "indices" list. However, I can't figure out how to bind the callback function to the index list. (Currently Callback is uselessly updating the s2 source with indices.)

The code should work when copied:

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

# Mock data
m = np.zeros((6,11))
for i in range(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(z=[]))
p2 = figure(plot_width=400, plot_height=400)    
m1 = ColumnDataSource(m1)
indices = [1,3,4]
for i in indices:
    p2.line(np.arange(0,11,1), '%s'%i ,  source=m1)

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

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

      

Original question:

Working with the example in the Bokeh documentation. I am trying to get the indices from a selection in the left window and use them to get the corresponding row from the matrix with the original data and plot the row. More details:

I start with a matrix of values ​​where each column is a year and each row is a location. I am running Sklearn Spectral Embedding on a matrix to characterize the data and get a matrix where each column describes the data in some way. I am plotting the first 3 columns as x, y coordinates and color. Then I try to modify the example so that when some points are selected, the second plot displays their original data (lines) as separate lines. The relevant code, taken mostly from the example, is shown below.

+3


source to share


2 answers


Having no answers either here or on the Bokeh mailing list, I find it is not possible to use a callback this way, so I had to work around it and accept the current limitation, which is the impossibility of making the "size" callback.

For data exploration, it would still be.



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 displays the original data for the first two indices in the selected data. The limitation is that the number of series of source data must be predefined, so the code will only build two lines. In addition, it also requires at least two lines to execute. It won't work for just one selected item. Therefore, if I predefine more lines to graph, I will always need to select that number of points.

+1


source


Let me know if I don't understand your question, but it looks like you have a matrix (let's call it m1), that you use transform and give data in s1 and draw in p1. You want to be able to select some data in p1 and have corresponding data in m1 in p2.

If that's correct, you need three ColumnDataSource objects. You will need to create a ColumnDataSource for m1 and use the s1 and s2 you already have. The callback should 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 cast the indices of the selected data and find x and y with the same indices m1 (raw data) and add them to s2. Then calling s2.trigger ('change') will update the data points in p2.

Let me know if I don't understand your question.

+1


source







All Articles