如何在Bokeh的MultiLine图元悬浮时显示单个对应数值?
解决Bokeh MultiLine HoverTool显示单个点精确值的问题
你遇到的这个问题确实是Bokeh中MultiLine glyph的默认行为导致的——因为每个数据条目都是列表,HoverTool会直接显示整个列表,而非当前悬停点的单个值。不用$x/$y(鼠标坐标)也不用额外不可见线条的话,有两种可靠方案,都能完美兼容筛选、联动等复杂功能:
方案一:自定义JS回调(适配MultiLine高性能场景)
如果你的图表包含大量线条,MultiLine的性能优势很重要,那么可以通过HoverTool的JavaScript回调动态捕获当前悬停点的精确值:
完整代码
from bokeh.plotting import show, figure from bokeh.models import ColumnDataSource, HoverTool, CustomJS df = { 'X_value': [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]], 'model': ['m1', 'm1', 'm2', 'm2'], 'color': ['red', 'red', 'blue', 'blue'], 'Y_value': [[0.50, 0.66, 0.70, 0.67], [0.65, 0.68, 0.71, 0.66], [0.80, 0.79, 0.84, 0.80], [0.80, 0.83, 0.76, 0.64]] } source = ColumnDataSource(df) p = figure(plot_height=400) multi_line_renderer = p.multi_line( xs='X_value', ys='Y_value', legend="model", color='color', line_width=5, line_alpha=0.6, hover_line_alpha=1.0, source=source ) # 配置带JS回调的HoverTool hover = HoverTool( show_arrow=False, line_policy='next', renderers=[multi_line_renderer], # 仅作用于当前MultiLine tooltips=[], # 初始为空,由JS动态填充 callback=CustomJS(args=dict(source=source), code=""" // 获取当前悬停的线条索引和点索引 const line_idx = cb_data.index.indices[0]; const point_idx = cb_data.index.index; // 从数据源中提取对应点的精确值 const x_val = source.data.X_value[line_idx][point_idx]; const y_val = source.data.Y_value[line_idx][point_idx]; const model_name = source.data.model[line_idx]; // 更新Hover提示内容 cb_obj.tooltips = [ ('Model', model_name), ('X_value', x_val), ('Y_value', y_val.toFixed(2)) // 可选:格式化小数位数 ]; """) ) p.add_tools(hover) show(p)
核心逻辑
- 通过
cb_data.index获取当前悬停的线条在数据源中的索引和点在该线条内的索引 - 从数据源的嵌套列表中提取对应点的精确值
- 动态更新HoverTool的提示内容,完全基于原始数据源操作,不影响筛选、联动功能
方案二:转换为长格式使用Line glyph(更直观)
如果你的线条数量不算特别多,可以将嵌套列表的数据源转换为扁平的长格式,用普通的Line glyph绘制,HoverTool就能直接识别单个点的值:
完整代码
import pandas as pd from bokeh.plotting import show, figure from bokeh.models import ColumnDataSource, HoverTool # 将原始嵌套数据转换为长格式DataFrame df_long = pd.DataFrame({ 'X_value': [1,2,3,4]*4, 'Y_value': [0.50,0.66,0.70,0.67,0.65,0.68,0.71,0.66,0.80,0.79,0.84,0.80,0.80,0.83,0.76,0.64], 'model': ['m1']*4 + ['m1']*4 + ['m2']*4 + ['m2']*4, 'color': ['red']*8 + ['blue']*8, 'line_id': [0]*4 + [1]*4 + [2]*4 + [3]*4 # 标记每条线的唯一ID }) source = ColumnDataSource(df_long) p = figure(plot_height=400) p.line( x='X_value', y='Y_value', legend="model", color='color', line_width=5, line_alpha=0.6, hover_line_alpha=1.0, source=source, group='line_id' # 按line_id分组绘制多条线 ) # 直接配置HoverTool即可显示单个点值 hover = HoverTool( show_arrow=False, line_policy='next', tooltips=[ ('Model', '@model'), ('X_value', '@X_value'), ('Y_value', '@Y_value') ] ) p.add_tools(hover) show(p)
优势
- 无需编写JS代码,逻辑更直观
- 长格式数据源天然支持Bokeh的各种筛选、联动组件,兼容性拉满
内容的提问来源于stack exchange,提问作者Dmitriy Apollonin
相关产品推荐
相关产品推荐

