如何在Bokeh悬停工具中显示图像glyph的像素对应直方图计数
解决方案:Bokeh 2.4.3 实现2D直方图Hover显示计数
纯Bokeh实现思路
Bokeh 2.4.3 没有image_index工具,需要通过自定义JS回调手动计算鼠标位置对应的bin索引,再从计数数组H中取值并显示在hover提示里。核心步骤是:
- 将计数数组
H、bin的起始值和步长传入数据源 - 用HoverTool的
customJS回调,把鼠标坐标转换为bin的行列索引 - 根据索引从
H中提取计数,更新hover提示内容
代码示例
import numpy as np from bokeh.plotting import figure, show, ColumnDataSource from bokeh.models import HoverTool, CustomJS # 生成测试数据 np.random.seed(42) x = np.random.normal(size=1000) y = np.random.normal(size=1000) # 计算2D直方图 H, xedges, yedges = np.histogram2d(x, y, bins=20) # 转置H匹配Bokeh的image渲染顺序(行优先对应y轴方向bin) H = H.T # 准备数据源:包含图像数据、bin参数和计数数组 source = ColumnDataSource(data={ 'image': [H], 'x': [xedges[0]], 'y': [yedges[0]], 'dw': [xedges[-1] - xedges[0]], 'dh': [yedges[-1] - yedges[0]], 'counts': [H.tolist()] # 转成列表方便JS读取 }) p = figure(width=600, height=600, tools='pan,box_zoom,reset') # 绘制2D直方图 p.image(image='image', x='x', y='y', dw='dw', dh='dh', source=source, palette='Viridis256') # 配置HoverTool:通过JS动态更新提示内容 hover = HoverTool( tooltips=[ ('x range', '@x_start - @x_end'), ('y range', '@y_start - @y_end'), ('count', '@count') ], callback=CustomJS(args=dict(source=source, xedges=xedges.tolist(), yedges=yedges.tolist()), code=""" const hover = cb_obj; const x_coord = hover.geometries[0].rx; const y_coord = hover.geometries[0].ry; // 计算bin的步长和索引 const x_step = xedges[1] - xedges[0]; const y_step = yedges[1] - yedges[0]; let x_idx = Math.floor((x_coord - xedges[0]) / x_step); let y_idx = Math.floor((y_coord - yedges[0]) / y_step); // 边界处理:避免索引越界 x_idx = Math.max(0, Math.min(x_idx, xedges.length - 2)); y_idx = Math.max(0, Math.min(y_idx, yedges.length - 2)); // 提取对应bin的计数 const count = source.data.counts[0][y_idx][x_idx]; // 更新hover提示文本 hover.tooltips[0] = ['x range', `${xedges[x_idx].toFixed(2)} - ${xedges[x_idx+1].toFixed(2)}`]; hover.tooltips[1] = ['y range', `${yedges[y_idx].toFixed(2)} - ${yedges[y_idx+1].toFixed(2)}`]; hover.tooltips[2] = ['count', count.toString()]; """) ) p.add_tools(hover) show(p)
Holoviews 实现思路
Holoviews 对2D直方图的交互支持更简洁,无需手动编写JS,直接通过Histogram2D结合内置HoverTool即可自动映射计数:
代码示例
import numpy as np import holoviews as hv from holoviews import opts hv.extension('bokeh') # 生成测试数据 np.random.seed(42) x = np.random.normal(size=1000) y = np.random.normal(size=1000) # 计算2D直方图的bin和计数 H, xedges, yedges = np.histogram2d(x, y, bins=20) # 创建Holoviews的2D直方图对象 hist2d = hv.Histogram2D((xedges, yedges, H), kdims=['x', 'y'], vdims='count') # 配置图表和hover提示 hist2d.opts( opts.Histogram2D( width=600, height=600, palette='Viridis256', tools=['hover', 'pan', 'box_zoom', 'reset'], tooltip=[('x', '@x_start{0.2f} - @x_end{0.2f}'), ('y', '@y_start{0.2f} - @y_end{0.2f}'), ('count', '@count')] ) ) # 渲染图表 hv.render(hist2d)
说明
- Holoviews 会自动处理bin索引与计数的映射逻辑,无需手动计算
- 生成的图表基于Bokeh后端,完全兼容现有Bokeh生态,代码量远少于纯Bokeh实现
内容的提问来源于stack exchange,提问作者Anirban Chakraborty
相关产品推荐
相关产品推荐

