Python Bokeh:在ColumnDataSource中编辑数据的某些方法之间有什么区别
我有一个关于Bokeh 2.3.0服务器应用程序中的Python Bokeh:在ColumnDataSource中编辑数据的某些方法之间有什么区别,python,bokeh,Python,Bokeh,我有一个关于Bokeh 2.3.0服务器应用程序中的ColumnDataSource的问题。 下面是一个例子,试图说明我的问题。虽然它要长一点,但我已经花了很多努力使它尽可能的小但尽可能的完整 因此,我知道在ColumnDataSource中编辑数据至少有两种主要的方法是可行的。 第一个是通过使用源代码使用“index\u方式”(我不知道如何正确调用此方法)。数据['my\u column\u name'][]='my\u new\u value',其中可能导致类似[0:10]或[[True,F
ColumnDataSource
的问题。下面是一个例子,试图说明我的问题。虽然它要长一点,但我已经花了很多努力使它尽可能的小但尽可能的完整 因此,我知道在
ColumnDataSource
中编辑数据至少有两种主要的方法是可行的。第一个是通过使用
源代码使用“index\u方式”(我不知道如何正确调用此方法)。数据['my\u column\u name'][]='my\u new\u value'
,其中
可能导致类似[0:10]
或[[True,False,True]
,等等。将数据子集为numpy数组。通过这种方式,可以使用源.selected.index
对数据进行索引
second方法使用的是ColumnDataSource
的。引用调用将其描述为在特定位置高效更新数据源列
我在代码中遇到的第三种方法是在ColumnDataSource
中编辑/更改一个完整的列,比如source.data['my\u data\u column\u 1']=source.data['my\u data\u column\u 2']
。这样,我可以将数据列设置为已经存在的数据列
我的问题是:它们的设计是否有不同的表现?我发现使用“index”方法的更改不会传播或更新到HoverTool,而对于其他两种方法,这似乎有效。在下面的代码示例中可以看到这种行为。更改绘图中的前几个样本时,通过使用选择工具选择样本并通过
source.data['Label']
viaLabel\u selected\u via\u index()
编辑,鼠标悬停工具不会显示“Label”的正确更新值。但是,数据中的更改是实际执行的,这可以通过访问和打印源数据['label']
的前几个样本的检查标签()
看到
当鼠标悬停在数据上时,使用其他方法之一更改标签
值确实会显示正确且更新的值
import pandas as pd
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, LinearColorMapper, Dropdown, Button, HoverTool
from bokeh.layouts import layout
import random
import time
plot_data = 'Value1'
LEN = 1000
df = pd.DataFrame({"ID":[i for i in range(LEN)],
"Value1":[random.random() for i in range(LEN)],
"Value2":[random.random() for i in range(LEN)],
"Color": [int(random.random()*10) for i in range(LEN)] })
df['plot_data'] = df[plot_data]
df['Label'] = "No Label Set"
df['Label_new_col'] = "Label was added"
source = ColumnDataSource(df)
cmap = LinearColorMapper(palette="Turbo256", low = 0, high = 3)
def make_tooltips():
return [('ID', '@ID'),
('Label', '@Label'),
(plot_data, f'@{plot_data}')]
tooltips = make_tooltips()
hover_tool = HoverTool(tooltips=tooltips)
plot1 = figure(plot_width=800, plot_height=250, tooltips=tooltips, tools='box_select')
plot1.add_tools(hover_tool)
circle = plot1.circle(x='ID', y='plot_data', source=source,
fill_color={"field":'Color', "transform":cmap},
line_color={"field":'Color', "transform":cmap})
def update_plot_data(event):
global plot_data
plot_data = event.item
source.data['plot_data'] = source.data[plot_data]
hover_tool.tooltips = make_tooltips()
dropdown = Dropdown(label='Change Value', menu=["Value1","Value2"])
dropdown.on_click(update_plot_data)
def label_selected_via_index(event):
t0 = time.time()
selected = source.selected.indices
source.data['Label'][0:10] = 'Label was added'
hover_tool.tooltips = make_tooltips()
source.selected.indices = []
print(f"Time needed for label_selected_via_index: {time.time()-t0:.5f}")
button_set_label1 = Button(label='Set Label via Index')
button_set_label1.on_click(label_selected_via_index)
def label_selected_via_patch(event):
t0 = time.time()
selected = source.selected.indices
patches = [(ind, 'Label was added') for ind in selected]
source.patch({'Label': patches})
hover_tool.tooltips = make_tooltips()
source.selected.indices = []
print(f"Time needed for label_selected_via_patch: {time.time()-t0:.5f}")
button_set_label2 = Button(label='Set Label via Patch')
button_set_label2.on_click(label_selected_via_patch)
def label_selected_via_new_col(event):
t0 = time.time()
selected = source.selected.indices
source.data['Label'] = source.data['Label_new_col']
hover_tool.tooltips = make_tooltips()
source.selected.indices = []
print(f"Time needed for label_selected_via_new_col: {time.time()-t0:.5f}")
button_set_label3 = Button(label='Set Label via New Column ')
button_set_label3.on_click(label_selected_via_new_col)
def check_label(event):
print(f"first 10 labels: {[l for l in source.data['Label'][0:10]]}")
button_label_check = Button(label='Check Label')
button_label_check.on_click(check_label)
layout_ = layout([[plot1],
[dropdown],
[button_set_label1 ,button_set_label2, button_set_label3],
[button_label_check]])
curdoc().add_root(layout_)
在我的应用程序中,我收集了大量数据并观察到,使用.patch()
确实比索引版本或替换完整列花费的时间要长得多。在我的应用程序中,索引方法需要不到一毫秒的时间,而补丁方法需要超过一秒钟的时间,这使得在交互更改值时,所有内容都有点滞后。基本上,我的应用程序在某种程度上类似于上面的示例,关于在一个绘图中选择样本并通过多个按钮分配标签的过程。这些标签也会通过工具提示显示在多重打印中,因此我需要进行此更新
有没有办法a)使索引版本也更新Hovertool?我更喜欢这种方法,因为它在视觉上要快得多,或者B)使.patch()
版本更快
我希望我能以某种方式理解我的问题,并感谢任何建议。在Bokeh服务器应用程序的上下文中,值得记住的是,“为了在浏览器中显示更改,实际需要做什么?”答案大致如下:
- 在Python中检测到(或发出信号)更改
- 更改事件被序列化并通过网络发送到浏览器
- 更改事件由BokehJS反序列化
- 将应用更改并更新浏览器中的视图
.prop_name=new_value
,字面上包括“点”和“等号”,则Bokeh可以自动神奇地检测到更改并将其发送到浏览器。以下是几个例子:
plot.title.text=“新建标题”#更新标题
glyph.line_color=“red”#更改glyph的线条颜色
slider.value=10#设置滑块的值
上面的示例都显示了基本的标量(字符串、数字)值,但这同样适用于更复杂的值。这种通用机制的另一个非常常见的例子是更新列数据源的整个.data
目录
source.data={'x':[…],'y':[…]}#字形或表格的新数据
这会更新CD中的所有数据,例如,线条图示符可能会重新绘制自身
根据您正在执行的操作、数据大小等,更新整个。数据dict可能会很昂贵(由于序列化、反序列化、网络传输等原因)。因此,在特定情况下,还有一些其他方法可能更有效
“就地”特殊情况
上面的区别特征是,一切都是一个“整体”分配,即没有突变或就地修改。在少数情况下,Bokeh可以自动神奇地处理对可变值的就地更新。不必太深究,到目前为止,最重要的例子是在ColumnDataSource
中设置一个新列,方法是在上使用标准的Python dict索引赋值
source.data['x']=[…]#Bokeh将自动处理此问题
这是您上面的第三种方法。它可以正常工作,但仅用于更新CD.data
dict中的列