You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bokeh中如何在HoverToolTip显示过滤后的DataFrame?

问题

在Bokeh 3.2.2版本中,尝试通过回调函数在Hover工具提示中显示过滤后的pandas DataFrame时,发现cb_data.source、cb_data.x、cb_data.y均为undefined。请问如何在JavaScript中获取当前的x、y属性?

错误信息

cb_data.source = undefined
cb_data.value = undefined
cb_data.x/cb_data.y = undefined/undefined

代码示例

import pandas as pd
from bokeh.models import HoverTool, ColumnDataSource, DatetimeTickFormatter, CustomJS
from bokeh.plotting import figure, show

data = {
    "Time": ["12:03", "12:03", "12:03", "12:03", "12:12", "12:12", "12:12", "12:12", "12:17", "12:17", "12:17", "12:17"],
    "Spot": [19653, 19653, 19653, 19653, 19629, 19629, 19629, 19629, 19640, 19640, 19640, 19640],
    "Strike": [19500, 19600, 19700, 19800, 19500, 19600, 19700, 19800, 19500, 19600, 19700, 19800],
    "cLtp": [99, 174, 167, 167, 91, 165, 161, 93, 95, 16, 12, 31],
    "pLtp": [49, 24, 42, 25, 51, 74, 36, 53, 21, 29, 75, 32],
    "cSellOi": [851500, 267500, 379050, 382350, 1324350, 389700, 438850, 1318250, 1365900, 449000, 433200, 433200],
    "pSellOi": [2530350, 1615600, 1656000, 1606500, 2542100, 1598450, 1646950, 2519050, 2544450, 1488200, 1460400, 1460400],
}

df = pd.DataFrame(data)

# Convert the 'Time' column to datetime
df['Time'] = pd.to_datetime(df['Time'], format='%H:%M')
df.sort_values(by='Time', inplace=True)

source = ColumnDataSource(df)

plot = figure(
    x_axis_label="Time",
    y_axis_label="Spot",
    x_axis_type="datetime",
    title="Dataframe in Tooltip",
)

plot.line(x='Time', y='Spot', source=source, line_width=3, line_color="blue")

plot.xaxis.formatter = DatetimeTickFormatter(
    days="%Y-%m-%d",
    hours="%H:%M:%S",
    minutes="%H:%M"
)

# Create a custom JavaScript callback for the hover tool
jsHover = """
var data = source.data;
var hover = hover;
hover.tooltips = [];
var tooltipString = ""


console.log( "cb_data.source = " + cb_data['source'] );
console.log( "cb_data.value = " + cb_data['value']);
console.log( "cb_data.x/cb_data.y = " + cb_data['x'] +"/" +  cb_data['y']);

for (var i = 0; i < data['Strike'].length; i++) {
    tooltipString += data['Strike'][i] + ": (" + data['cLtp'][i] + ", " + data['pLtp'][i] + ")[";
    tooltipString += data['cSellOi'][i] + ", " + data['pSellOi'][i] + "]\\n";
}

hover.tooltips = tooltipString;

hover.change.emit();
console.log('jsHover Callback Finished');
console.log(hover.tooltips);
"""

hoverTool = HoverTool(tooltips=None)
cb = CustomJS(args={'source': source,'hover': hoverTool}, code=jsHover)
hoverTool.callback = cb
plot.add_tools(hoverTool)
show(plot)

解决方案

1. 适配Bokeh 3.x的Hover回调参数

Bokeh 3.x版本中cb_data的结构已变更,不再直接提供x、y字段,需通过cb_data.geometry获取鼠标屏幕坐标,再结合图表的刻度转换方法得到实际数据值。

2. 修改后的完整实现代码

import pandas as pd
from bokeh.models import HoverTool, ColumnDataSource, DatetimeTickFormatter, CustomJS
from bokeh.plotting import figure, show

data = {
    "Time": ["12:03", "12:03", "12:03", "12:03", "12:12", "12:12", "12:12", "12:12", "12:17", "12:17", "12:17", "12:17"],
    "Spot": [19653, 19653, 19653, 19653, 19629, 19629, 19629, 19629, 19640, 19640, 19640, 19640],
    "Strike": [19500, 19600, 19700, 19800, 19500, 19600, 19700, 19800, 19500, 19600, 19700, 19800],
    "cLtp": [99, 174, 167, 167, 91, 165, 161, 93, 95, 16, 12, 31],
    "pLtp": [49, 24, 42, 25, 51, 74, 36, 53, 21, 29, 75, 32],
    "cSellOi": [851500, 267500, 379050, 382350, 1324350, 389700, 438850, 1318250, 1365900, 449000, 433200, 433200],
    "pSellOi": [2530350, 1615600, 1656000, 1606500, 2542100, 1598450, 1646950, 2519050, 2544450, 1488200, 1460400, 1460400],
}

df = pd.DataFrame(data)
df['Time'] = pd.to_datetime(df['Time'], format='%H:%M')
df.sort_values(by='Time', inplace=True)
source = ColumnDataSource(df)

plot = figure(
    x_axis_label="Time",
    y_axis_label="Spot",
    x_axis_type="datetime",
    title="Dataframe in Tooltip",
)

plot.line(x='Time', y='Spot', source=source, line_width=3, line_color="blue")

plot.xaxis.formatter = DatetimeTickFormatter(
    days="%Y-%m-%d",
    hours="%H:%M:%S",
    minutes="%H:%M"
)

# 调整后的JavaScript回调
jsHover = """
var data = source.data;
var hover = hover;
var plot = plot;

// 将鼠标屏幕坐标转换为数据域的x、y值
var x = plot.x_scale.invert(cb_data.geometry.x);
var y = plot.y_scale.invert(cb_data.geometry.y);

// 对齐时间戳精度(pandas存储的datetime为毫秒级)
var targetTime = Math.round(x.getTime() / 1000) * 1000;
var targetSpot = Math.round(y);

// 构建格式化的提示文本
var tooltipString = `<div>时间: ${new Date(targetTime).toLocaleTimeString()}</div>`;
tooltipString += `<div>Spot: ${targetSpot}</div>`;
tooltipString += "<div>---</div>";

// 筛选匹配当前时间和Spot的记录
for (var i = 0; i < data['Time'].length; i++) {
    if (data['Time'][i] === targetTime && data['Spot'][i] === targetSpot) {
        tooltipString += `<div>${data['Strike'][i]}: (${data['cLtp'][i]}, ${data['pLtp'][i]})[${data['cSellOi'][i]}, ${data['pSellOi'][i]}]</div>`;
    }
}

hover.tooltips = [["", tooltipString]];
hover.change.emit();
"""

hoverTool = HoverTool(tooltips=None)
# 必须传入plot对象用于刻度转换
cb = CustomJS(args={'source': source, 'hover': hoverTool, 'plot': plot}, code=jsHover)
hoverTool.callback = cb
plot.add_tools(hoverTool)
show(plot)

关键要点

  • 需将plot对象传入CustomJS的参数中,才能调用x_scale.invert和y_scale.invert完成坐标转换
  • 时间值需要做毫秒级对齐,匹配ColumnDataSource中存储的时间戳格式
  • 动态提示文本需用HTML格式,以数组形式赋值给hover.tooltips

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:40:58