You need to enable JavaScript to run this app. Bokeh 2.3.0 Server中ColumnDataSource数据编辑方法差异及优化咨询
优惠活动
大模型
产品
解决方案
定价
更多

Bokeh 2.3.0 Server中ColumnDataSource数据编辑方法差异及优化咨询

问题根因说明

Bokeh 的 ColumnDataSource 仅在对 source.data 字典的顶层键值对进行重新赋值时,才会触发数据变更事件,将更新同步推送到前端,驱动 HoverTool、图形渲染等组件刷新内容。
使用索引修改法时,本质是对 source.data['Label'] 内部存储的类numpy数组做原地切片修改,属于数组内部的操作,不会被 Bokeh 的变更检测逻辑捕获——因此后端读取数据源的值已经更新,但前端始终缓存的是旧的列数据,自然不会同步刷新 HoverTool 的提示内容。

方案A:零性能损失的索引修改法适配(首选)

完全保留原有索引修改法的毫秒级性能,仅需增加1行代码触发变更检测,即可实现 HoverTool 的同步更新:

  1. 全程保留原有的原地numpy索引修改逻辑,不做任何改动,性能和之前完全一致
  2. 原地修改完成后,对被修改的列做一次顶层引用重赋值——该操作仅传递数组引用,无数据拷贝,开销可忽略
    修改后的核心代码示例:
def label_selected_via_index(event):
    t0 = time.time()
    selected = source.selected.indices
    # 原有原地索引修改逻辑,性能不损失
    source.data['Label'][selected] = 'Label was added'
    # 新增:触发顶层属性变更检测,推送最新列数据到前端
    source.data['Label'] = source.data['Label']
    # 移除冗余的tooltips重赋值操作:只要列数据更新,HoverTool会自动读取最新值
    source.selected.indices = []
    print(f"Time needed for label_selected_via_index: {time.time()-t0:.5f}")

注意:

  • 该方案的整体耗时和原有的纯索引修改法几乎无差异,因为numpy数组的原地切片修改本身是C层优化的操作,引用重赋值仅为指针传递,没有额外的Python层循环或数据拷贝开销
  • 原示例中索引修改函数硬编码修改了前10个样本,和选中的样本没有关联,实际使用时可以把硬编码的索引替换为source.selected.indices,实现选中样本的标签更新
  • 不需要额外做整列数据拷贝,避免不必要的序列化和带宽开销

方案B:patch() 方法的性能优化

patch() 方法耗时过高的核心原因,是该方法的设计初衷是修改零散的少量数据点,当批量给数百/数千个选中点赋同一个固定值时,逐点构建patch元组的全Python层操作会带来指数级的性能开销。可通过以下方式优化:

  • 避免逐点构建patch元组:Bokeh 的 patch() 支持直接传入切片对象作为更新位置,不需要为索引范围内的每个点单独创建元组。例如批量更新0-999索引的Label值,直接传(slice(0,1000), 'Label was added')即可,可减少99%的元组构建开销
  • 移除冗余的tooltips重赋值操作:原代码中每次修改数据后都重新给hover_tool.tooltips赋值的操作,会触发HoverTool组件的全量重建,带来不必要的耗时。只要列数据成功推送到前端,HoverTool在悬停时会自动读取最新的@Label值,完全不需要重复设置tooltips
  • 场景适配替换:如果操作是批量给选中的点赋同一个固定标签,patch() 本质是性能最差的选择——它的序列化开销比直接推整列数据还高,此时优先使用方案A的索引修改+引用重赋值逻辑,性能可提升两个数量级以上

内容的提问来源于stack exchange,提问作者Crysers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.29 05:36:04