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

如何在Bokeh中为多ColumnDataSource数据配置HoverTool及相关功能?

Bokeh散点图优化与问题解决

目标

基于包含x、y、type(取值为a、b、c)三列的pandas DataFrame生成散点图,要求:

  • 数据点按type显示不同颜色
  • 所有数据点均有Hover效果
  • type为c的数据点额外添加Tap效果

数据文件data_file.csv示例:

xytype
14a
23b
32a
44c
......

我的尝试

导入DataFrame并拆分为type为c的数据集和其余数据集,创建两个ColumnDataSource绘图,但存在两个未解决问题,同时想知道是否有更优实现方式。

代码

from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, OpenURL, TapTool
from bokeh.models.tools import HoverTool
from bokeh.transform import factor_cmap
import pandas as pd  # 补充原代码缺失的pandas导入

file = "data.csv"

df = pd.read_csv(file, skiprows=1, header=None, sep="\t")      

# 拆分数据集:a&b为一组,c为另一组
c_df = df.drop(df[df[2] != 'c'].index)
ab_df = df.drop(df[df[2] == 'c'].index)

ab_source = ColumnDataSource(data=dict(                
                Independent = ab_df[0],                           
                Dependent = ab_df[1],                        
                Type = ab_df[2]
            ))
c_source = ColumnDataSource(data=dict(                
                Independent = c_df[0],                           
                Dependent = c_df[1],                        
                Type = c_df[2],
                link = "http://example.com/" + c_df[0].apply(str) + ".php"
            ))

p = figure(title="Random PLot")

p.circle('Independent', 'Dependent',                                          
                size=10,                                           
                source=ab_source,                                     
                color=factor_cmap('Type',
                                ['red', 'blue'],
                                ['a', 'b']),
                legend_group='Type'
            )

p.circle('Independent', 'Dependent',               
                size=12,               
                source=c_source,
                color=factor_cmap('Type',
                                ['green'],
                                ['c']),
                name='needsTapTool'
            )

p.legend.title = "Type"


hover = HoverTool()     
hover.tooltips = """          <div>
            <h3>Type: @Type</h3>
            <p> @Independent and @Dependent </p>
          </div>
                 """
p.add_tools(hover)

url = "@link"                              
tap = TapTool(names=['needsTapTool'])         
tap.callback = OpenURL(url=url)
p.add_tools(tap)

show(p)

问题解答

1. 实现不同类型数据点的差异化Hover效果

Bokeh的HoverTool支持通过names参数指定作用的图形元素,以此实现分组差异化提示:

  • 给a&b组的circle添加name标识,比如name='ab_points'
  • 创建两个HoverTool实例,分别绑定对应names并设置不同tooltips

示例代码片段:

# 绘制a&b组时添加name参数
p.circle('Independent', 'Dependent',                                          
                size=10,                                           
                source=ab_source,                                     
                color=factor_cmap('Type',
                                ['red', 'blue'],
                                ['a', 'b']),
                legend_group='Type',
                name='ab_points'  # 添加分组标识
            )

# 针对a&b组的HoverTool
hover_ab = HoverTool(names=['ab_points'])     
hover_ab.tooltips = """
          <div>
            <h3>Type: @Type</h3>
            <p>X: @Independent, Y: @Dependent</p>
          </div>
                 """

# 针对c组的HoverTool
hover_c = HoverTool(names=['needsTapTool'])     
hover_c.tooltips = """
          <div>
            <h3>Type: @Type</h3>
            <p>X: @Independent, Y: @Dependent</p>
            <p>可点击跳转</p>
          </div>
                 """

# 添加两个HoverTool
p.add_tools(hover_ab, hover_c)

2. 截取ColumnDataSource中的数据生成新字段

不要直接在ColumnDataSource中处理数据,应先在pandas DataFrame层面完成转换,再传入数据源:

  • 假设原数据有带http://www前缀的href列,先通过pandas字符串方法清理:
# 假设df[3]是带前缀的href列
df['clean_link'] = df[3].str.replace('http://www.', '', regex=False)

# 拆分c_df时保留处理后的字段
c_df = df[df[2] == 'c'].copy()  # 用copy避免链式索引警告

# 构建数据源时直接使用清理后的字段
c_source = ColumnDataSource(data=dict(                
                Independent = c_df[0],                           
                Dependent = c_df[1],                        
                Type = c_df[2],
                link = c_df['clean_link']
            ))

如果是拼接链接,建议在DataFrame中完成操作,避免索引错误:

c_df['link'] = "http://example.com/" + c_df[0].astype(str) + ".php"

更优实现方式:无需拆分数据集

可以只用一个ColumnDataSource,通过多次绘制circle分别对应不同type,每个图形元素设置独立name来绑定工具,代码更简洁:

import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, OpenURL, TapTool, HoverTool
from bokeh.transform import factor_cmap

# 读取数据并命名列
df = pd.read_csv("data.csv", skiprows=1, header=None, sep="\t", names=['x', 'y', 'type'])
# 预处理link字段
df['link'] = "http://example.com/" + df['x'].astype(str) + ".php"

source = ColumnDataSource(df)

p = figure(title="Random Plot")

# 绘制type=a的点
p.circle('x', 'y', size=10, source=source,
         color=factor_cmap('type', ['red'], ['a']),
         legend_group='type', name='a_points')

# 绘制type=b的点
p.circle('x', 'y', size=10, source=source,
         color=factor_cmap('type', ['blue'], ['b']),
         legend_group='type', name='b_points')

# 绘制type=c的点
p.circle('x', 'y', size=12, source=source,
         color=factor_cmap('type', ['green'], ['c']),
         legend_group='type', name='c_points')

p.legend.title = "Type"

# a、b组的Hover效果
hover_ab = HoverTool(names=['a_points', 'b_points'])
hover_ab.tooltips = """
<div>
  <h3>Type: @type</h3>
  <p>X: @x, Y: @y</p>
</div>
"""

# c组的Hover和Tap效果
hover_c = HoverTool(names=['c_points'])
hover_c.tooltips = """
<div>
  <h3>Type: @type</h3>
  <p>X: @x, Y: @y</p>
  <p>点击跳转</p>
</div>
"""

tap_c = TapTool(names=['c_points'])
tap_c.callback = OpenURL(url="@link")

p.add_tools(hover_ab, hover_c, tap_c)

show(p)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 13:03:23