让Bokeh Callback更新列表而不是ColumnDataSource吗?

时间:2021-11-21 01:22:04

My question has to do with modifying this example from the Bokeh Gallery.

我的问题与从Bokeh Gallery修改这个例子有关。

I have a matrix m or raw data, in which every row corresponds to a pair of x,y coordinates in a transformed space (see #MockData part of code).

我有一个矩阵m或原始数据,其中每一行对应一个变换空间中的一对x,y坐标(参见代码的#MockData部分)。

The x,y coordinates are plotted on the left plot. I am trying to change the example so that, when I select some points in the left figure, the right figure will display line plots of the corresponding rows.

x,y坐标绘制在左图上。我正在尝试更改示例,以便当我在左图中选择一些点时,右图将显示相应行的线图。

I've narrowed the problem down to the point, where the right figure will show the desired data as specified by the "indices" list. I cannot, however, figure out how to link the Callback function to the indices list. (Currently the Callback uselessly updates source s2 with the indices.)

我已经将问题缩小到了这一点,正确的数字将显示“索引”列表指定的所需数据。但是,我无法弄清楚如何将回调函数链接到索引列表。 (目前回调无用地使用索引更新源s2。)

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 Bokeh documentation. I am trying to get indices from the selection in the left window and use them to get appropriate row from a matrix with original data and plot the row. In detail:

I start with a matrix of values, where each column is a year and each row a location. I run a sklearn Spectral Embedding on the matrix to characterize the data and get a matrix, where each column somehow describes the data. I plot the first 3 columns as x,y coordinates and a color. Next I am trying to modify the example such that, when I select some points, the second graph displays their original data (rows) as separate lines. The relevant code mostly taken from the example is below.

使用Bokeh文档中的示例。我试图从左侧窗口中的选择中获取索引并使用它们从具有原始数据的矩阵中获取适当的行并绘制该行。详细说明:我从一个值矩阵开始,每列都是一年,每一行都是一个位置。我在矩阵上运行了一个sklearn Spectral Embedding来表征数据并获得一个矩阵,其中每列以某种方式描述数据。我将前3列绘制为x,y坐标和颜色。接下来我试图修改这个例子,当我选择一些点时,第二个图形将它们的原始数据(行)显示为单独的行。主要从示例中获取的相关代码如下。

2 个解决方案

#1


1  

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.

让我知道如果我误解了你的问题,但听起来你有一个矩阵(我们称之为m1),你正在运行转换并在s1中产生数据并在p1中绘图。您希望能够选择p1中的某些数据,并将m1中的相应数据绘制在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:

如果这是正确的,那么您将需要三个ColumnDataSource对象。你将不得不为m1创建一个ColumnDataSource并使用你已经拥有的s1和s2。回调将需要是这样的:

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.

这将获取所选数据的索引,并找到具有相同索引m1(原始数据)的x和y,并将其添加到s2。然后s2.trigger('change')调用将更新p2中的数据点。

Let me know if I'm misunderstanding your question.

如果我误解你的问题,请告诉我。

#2


1  

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.

在这里或在Bokeh邮件列表中没有任何回复,让我相信以这种方式使用回调是不可能的,所以我不得不解决它并接受当前的限制,即无法使回调“大小”动态。

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.

代码现在绘制所选数据中前两个索引的原始数据。限制是必须预定义原始数据系列的数量,因此代码将只绘制两行。此外,它还需要至少两行才能执行。它不适用于仅选择的一个点。因此,如果我预定义更多的线要绘制,我将始终选择该点数。

#1


1  

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.

让我知道如果我误解了你的问题,但听起来你有一个矩阵(我们称之为m1),你正在运行转换并在s1中产生数据并在p1中绘图。您希望能够选择p1中的某些数据,并将m1中的相应数据绘制在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:

如果这是正确的,那么您将需要三个ColumnDataSource对象。你将不得不为m1创建一个ColumnDataSource并使用你已经拥有的s1和s2。回调将需要是这样的:

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.

这将获取所选数据的索引,并找到具有相同索引m1(原始数据)的x和y,并将其添加到s2。然后s2.trigger('change')调用将更新p2中的数据点。

Let me know if I'm misunderstanding your question.

如果我误解你的问题,请告诉我。

#2


1  

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.

在这里或在Bokeh邮件列表中没有任何回复,让我相信以这种方式使用回调是不可能的,所以我不得不解决它并接受当前的限制,即无法使回调“大小”动态。

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.

代码现在绘制所选数据中前两个索引的原始数据。限制是必须预定义原始数据系列的数量,因此代码将只绘制两行。此外,它还需要至少两行才能执行。它不适用于仅选择的一个点。因此,如果我预定义更多的线要绘制,我将始终选择该点数。