常见问题与故障排查
本节汇总了使用 Bokeh 过程中最常遇到的问题及其解决方案,并提供了系统化的调试方法。
安装与环境问题
问题1:安装失败
症状:pip install bokeh 报错或安装后无法导入
可能原因:
- Python 版本不兼容
- 依赖库冲突
- 网络问题导致下载中断
- 虚拟环境配置问题
解决方案:
# 检查 Python 版本(Bokeh 3.x 需要 Python 3.9+)
python --version
# 升级 pip 并安装
pip install --upgrade pip
pip install bokeh -i https://pypi.tuna.tsinghua.edu.cn/simple
# 如果有依赖冲突,尝试全新安装
pip install --force-reinstall bokeh
# 使用 conda 安装(推荐,自动处理依赖)
conda install bokeh -c conda-forge
# 验证安装
python -c "import bokeh; print(bokeh.__version__)"
问题2:JupyterLab 扩展问题
症状:JupyterLab 中图表不显示或显示空白
可能原因:
jupyter_bokeh未安装或版本过旧- JupyterLab 与
jupyter_bokeh版本不兼容 - Notebook 环境未正确初始化
- 旧版 JupyterLab 扩展缓存异常
解决方案:
# 安装或升级 jupyter_bokeh
pip install --upgrade jupyter_bokeh
# 如果你使用 JupyterLab 4.x,建议搭配 jupyter_bokeh 4.x 或更高版本
pip install "jupyter_bokeh>=4.0"
然后重启 JupyterLab,并在执行 show() 之前确认已经运行:
from bokeh.io import output_notebook
output_notebook()
说明:JupyterLab 3/4 通常不需要手动执行
jupyter lab build。只有在较旧的 JupyterLab 环境或扩展缓存异常时,才考虑执行jupyter lab clean && jupyter lab build。
验证扩展是否正常:
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
output_notebook()
p = figure()
p.circle([1, 2, 3], [4, 5, 6])
show(p)
问题3:Selenium / WebDriver 配置问题
症状:export_png() 或 export_svg() 报错
常见错误信息:
RuntimeError: Neither firefox and geckodriver nor chrome and chromedriver are available
解决方案:
# 推荐:使用 conda 一键安装
conda install selenium geckodriver firefox -c conda-forge
# 或使用 pip + 手动安装 WebDriver
pip install selenium
brew install chromedriver # macOS ChromeDriver
brew install geckodriver # macOS geckodriver(Firefox)
手动指定 WebDriver 路径:
from bokeh.io import export_png
from bokeh.plotting import figure
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.headless = True
driver = webdriver.Chrome(options=options)
p = figure()
p.circle([1, 2, 3], [4, 5, 6])
export_png(p, filename="chart.png", webdriver=driver)
driver.quit()
问题4:虚拟环境问题
解决方案:使用 python -m venv bokeh_env 或 conda create -n bokeh_env python=3.11 创建虚拟环境后 pip install bokeh。确保 which bokeh 指向虚拟环境中的 bokeh。
显示与渲染问题
问题5:图表不显示
症状:运行代码后没有显示图表
可能原因:
- 忘记调用
show(p)或output_notebook() - Jupyter 扩展未安装
- 输出模式冲突
解决方案:
from bokeh.io import output_notebook, output_file, show, save
# 在 Jupyter 中
output_notebook()
show(p)
# 在脚本中
output_file("chart.html")
show(p)
# 如果 show() 不工作,用 save() 替代
save(p)
print("图表已保存到 chart.html")
注意:
output_file()和output_notebook()不要同时使用。在 VS Code 中如果仍不显示,可使用save(p)后用浏览器打开。
问题6:图表显示空白
症状:Figure 画布出现了,但看不到数据
检查清单:
# 1. 确认数据不为空
source = ColumnDataSource(data={'x': [1, 2, 3], 'y': [4, 5, 6]})
print(f"数据行数: {len(source)}")
# 2. 检查坐标轴范围
p = figure()
p.circle([1, 2, 3], [4, 5, 6])
print(f"X 范围: {p.x_range.start} ~ {p.x_range.end}")
# 如果范围不对,手动设置
p.x_range.start = 0
p.x_range.end = 4
# 3. 检查 NaN 值
import numpy as np
data = [1, 2, np.nan, 4]
print(f"NaN 数量: {sum(np.isnan(data))}")
# 4. 确认 glyph 参数正确
p.circle('x', 'y', source=source) # 需要 x, y 和 source 三个参数
问题7:图表尺寸异常
症状:图表太大、太小或响应式布局不工作
sizing_mode 选项一览:
| 模式 | 说明 |
|---|---|
"fixed" | 固定尺寸(默认) |
"stretch_width" | 宽度随容器拉伸 |
"stretch_height" | 高度随容器拉伸 |
"stretch_both" | 宽高都拉伸 |
"scale_width" | 等比缩放,宽度填满容器 |
"scale_height" | 等比缩放,高度填满容器 |
"scale_both" | 等比缩放,宽高都填满 |
# 固定尺寸
p = figure(width=800, height=400)
# 响式(推荐用于 Web 嵌入)
p = figure(sizing_mode="stretch_width", height=400)
# 布局也支持 sizing_mode
from bokeh.layouts import row
layout = row(p1, p2, sizing_mode="stretch_width")
问题8:工具栏不显示
# toolbar_location: "above", "below", "left", "right", None
p = figure(toolbar_location="above", tools="pan,wheel_zoom,box_zoom,reset,save")
p.toolbar.logo = None # 隐藏 Bokeh logo
p.toolbar.autohide = True # 鼠标不在图表上时自动隐藏
问题9:颜色显示错误
Bokeh 支持的颜色格式:
| 格式 | 示例 |
|---|---|
| CSS 颜色名称 | "red" |
| 十六进制 | "#FF5733" |
| RGB 元组(0~255) | (255, 87, 51) |
| RGBA 元组 | (255, 87, 51, 0.5) |
常见错误:Bokeh 不支持 matplotlib 的
(0.5, 0.3, 0.1)格式(0~1 的 RGB),请使用 0~255 的 RGB 或十六进制。
from bokeh.transform import linear_cmap
from bokeh.palettes import Viridis256, Category20
# 确保 low 和 high 覆盖数据范围
color_mapper = linear_cmap('value', Viridis256,
low=min(data['value']), high=max(data['value']))
# 超过 10 种颜色时用 Category20
colors = Category20[20][:15]
问题10:图例重叠
# 方式1:调整位置
p.legend.location = "top_left"
# 方式2:移到图表外部
p.add_layout(p.legend[0], 'right')
# 方式3:点击图例隐藏/显示数据
p.legend.click_policy = "hide" # 或 "mute"(变淡)
# 附加:设置图例样式
p.legend.background_fill_alpha = 0.5
p.legend.border_line_color = None
p.legend.label_text_font_size = "10pt"
数据相关问题
问题11:中文显示问题
症状:图表中的中文显示为方框或乱码
解决方案:
# 方法1:设置具体字体
p.title.text_font = "SimHei"
p.xaxis.axis_label_text_font = "SimHei"
p.yaxis.axis_label_text_font = "SimHei"
# 常用中文字体:
# Windows: "SimHei"(黑体), "Microsoft YaHei"(微软雅黑)
# macOS: "PingFang SC", "Heiti SC"
# Linux: "WenQuanYi Micro Hei"
# 方法2:使用主题全局设置
from bokeh.themes import Theme
theme = Theme(json={
'attrs': {
'Figure': {
'title_text_font': 'PingFang SC'
},
'Axis': {
'axis_label_text_font': 'PingFang SC',
'major_label_text_font': 'PingFang SC'
},
'Legend': {
'label_text_font': 'PingFang SC'
}
}
})
from bokeh.io import curdoc
curdoc().theme = theme
# 方法3:查看系统可用字体
import matplotlib.font_manager as fm
fonts = [f.name for f in fm.fontManager.ttflist]
chinese_fonts = [f for f in fonts if any(
keyword in f for keyword in ['Hei', 'Song', 'Ming', 'Ya', 'Ping', 'Yuan', 'Fang']
)]
print("可用中文字体:", chinese_fonts)
问题12:ColumnDataSource 错误
症状:ValueError: expected an element of either String or Dict(String, Either(String, Seq(String))) 等数据相关报错
常见错误与解决:
from bokeh.models import ColumnDataSource
import pandas as pd
# 错误1:列长度不一致
source = ColumnDataSource(data={
'x': [1, 2, 3],
'y': [4, 5, 6], # ✅ 所有列长度必须相同
})
# 错误2:引用不存在的列
print(source.column_names) # 先确认列名
p.circle('x', 'y', source=source)
# 错误3:DataFrame 中有 NaN
df = pd.DataFrame({'x': [1, 2, None], 'y': [4, 5, 6]})
df = df.dropna() # ✅ 填充或删除 NaN
# 错误4:更新数据方式不对
source.data['x'] = [10, 20] # ❌ 直接修改不会触发更新
source.data = {'x': [10, 20]} # ✅ 整体赋值触发更新
source.stream({'x': [4], 'y': [7]}) # ✅ 追加数据
source.patch({'x': [(0, 100)]}) # ✅ 局部更新(索引0改为100)
问题13:日期轴格式问题
症状:日期显示为时间戳数字,或格式不正确
解决方案:
from bokeh.plotting import figure
from bokeh.models import DatetimeTickFormatter, HoverTool
import pandas as pd
# 方法1:使用 x_axis_type="datetime"
dates = pd.date_range('2024-01-01', periods=12, freq='M')
values = np.random.randn(12).cumsum()
p = figure(x_axis_type="datetime", width=800, height=400)
p.line(dates, values, line_width=2)
# 方法2:自定义日期格式
p.xaxis.formatter = DatetimeTickFormatter(
days=["%Y-%m-%d"], months=["%Y-%m"],
hours=["%H:%M"], minutes=["%H:%M"],
)
# 方法3:HoverTool 中的日期格式
hover = HoverTool(tooltips=[
("日期", "@date{%F}"),
("值", "@value{0.00}"),
], formatters={'@date': 'datetime'})
# 方法4:确保传入 datetime 类型
dates = pd.to_datetime(['2024-01-01', '2024-02-01', '2024-03-01']) # ✅
问题14:分类轴问题
症状:类别顺序错误、标签重叠
解决方案:使用 x_range 参数控制类别顺序:p = figure(x_range=['C', 'A', 'B'])。标签重叠时设置 p.xaxis.major_label_orientation = "vertical" 或 math.pi / 4。注意分类数据必须配合 x_range 使用,不能直接当数值轴用。
问题15:NaN / None 值处理
症状:图表部分数据缺失或渲染异常
解决方案:使用 df.dropna() 删除或 df.fillna(0) / df.interpolate() 填充。注意 Bokeh 折线遇到 NaN 会断开,这是预期行为;如需连续可先过滤:mask = ~np.isnan(y); p.line(x[mask], y[mask])。
Bokeh Server 问题
问题16:Bokeh Server 无法启动
症状:运行 bokeh serve app.py 报错
可能原因:
- 端口被占用
- 文件路径错误
- 代码语法错误
解决方案:
# 检查并释放端口
lsof -i :5006 # macOS/Linux
kill -9 $(lsof -t -i:5006)
# 先用最直接的方式验证应用能否启动
bokeh serve app.py --show
# 使用其他端口
bokeh serve app.py --port 5007 --show
# 查看详细错误信息
bokeh serve app.py --log-level debug
# 以目录方式运行(推荐)
mkdir myapp && mv app.py myapp/main.py
bokeh serve myapp --show
注意:如果你的示例里使用了
curdoc()、on_change()、add_periodic_callback()等 Python 回调,必须通过bokeh serve启动;直接运行python app.py或在静态show()/save()模式下,这些回调都不会生效。
问题17:回调不触发
症状:滑块、按钮等控件的回调函数没有被执行
可能原因:
- 回调函数签名错误
on_change监听的属性名不对- 在静态 HTML 中使用了 Python 回调
- Bokeh Server 未正确启动
解决方案:
from bokeh.io import curdoc
from bokeh.models import Slider, Button, CustomJS
from bokeh.plotting import figure
# 问题1:on_change 回调签名必须接受三个参数
# ❌ 错误
def update(new_value):
pass
# ✅ 正确
def update(attr, old, new):
print(f"属性 {attr} 从 {old} 变为 {new}")
slider = Slider(start=0, end=10, value=1)
slider.on_change('value', update)
# 问题2:on_click 回调签名
def on_button_click():
print("clicked")
button = Button(label="Click")
button.on_click(on_button_click)
# 问题3:静态 HTML 不支持 Python 回调
# ❌ 在 show() / save() 中使用 on_change → 不会执行
# ✅ 静态 HTML 只能用 CustomJS
slider.js_on_change('value', CustomJS(code="""
console.log('新值: ' + cb_obj.value);
"""))
# 问题4:确保 curdoc() 正确使用
# 下面这类代码需要在 `bokeh serve app.py --show` 启动的应用里运行
curdoc().add_root(layout) # 添加到文档
# 不要再调用 show()
问题18:WebSocket 连接失败
症状:浏览器控制台报 WebSocket 错误,图表无法交互
常见错误信息:
WebSocket connection to 'ws://localhost:5006/app/ws' failed
解决方案:
# 原因1:WebSocket 源未允许
bokeh serve app.py --allow-websocket-origin=localhost:5006
bokeh serve app.py --allow-websocket-origin=example.com
# 允许所有源(仅开发环境)
bokeh serve app.py --allow-websocket-origin=*
# 原因2:Nginx 反向代理需要 WebSocket 配置
location / {
proxy_pass http://127.0.0.1:5006;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_read_timeout 86400;
}
# 原因3:SSL/HTTPS 混合内容 → Bokeh Server 也需要 HTTPS 或用反向代理
# 原因4:防火墙阻断 → sudo ufw allow 5006
问题19:多用户会话冲突
症状:多个用户同时访问时数据互相干扰
解决方案:
# ❌ 错误:使用全局变量(所有会话共享)
global_source = ColumnDataSource(data={'x': [1, 2, 3]})
def update(attr, old, new):
global_source.data = ... # 会影响所有用户!
# ✅ 正确:在 make_document 函数中创建局部对象
def make_document(doc):
source = ColumnDataSource(data={'x': [1, 2, 3]})
p = figure()
p.circle('x', 'y', source=source)
def update(attr, old, new):
source.data = ... # 只影响当前会话
slider = Slider(start=0, end=10, value=1)
slider.on_change('value', update)
doc.add_root(column(slider, p))
# 启动方式
from bokeh.server.server import Server
server = Server({'/': make_document})
server.start()
问题20:部署后样式丢失
症状:部署到生产环境后图表样式异常或 BokehJS 加载失败
解决方案:CDN 不可访问时,使用内联资源模式:output_file("chart.html", resources=INLINE)。同时确保静态文件目录权限正确(chmod -R 755),并在 Nginx 中配置 CORS:add_header Access-Control-Allow-Origin *;。
导出与输出问题
问题21:大数据集性能问题
症状:图表渲染缓慢或浏览器崩溃
快速修复:启用 WebGL 渲染:p = figure(output_backend="webgl")
关于性能优化的完整讨论(WebGL、数据采样、降采样算法、Datashader 等),详见第九章。
问题22:导出 PNG 失败
症状:export_png() 报错或超时
解决方案:
from bokeh.io import export_png
from bokeh.plotting import figure
import selenium
driver = selenium.webdriver.Chrome()
driver.set_page_load_timeout(30) # 增加超时时间
p = figure()
p.circle([1, 2, 3], [4, 5, 6])
try:
export_png(p, filename="chart.png", webdriver=driver, timeout=30)
except Exception as e:
print(f"导出失败: {e}")
finally:
driver.quit()
# 高分辨率导出:使用 2 倍尺寸
p = figure(width=1600, height=800)
export_png(p, filename="chart_hires.png")
问题23:SVG 导出限制
- 需要先设置
p = figure(output_backend="svg"),然后调用export_svg(p, filename="chart.svg") - 不支持 WebGL 渲染的内容
- 部分交互工具(如
lasso_select)在 SVG 中不工作 - 数据量大时 SVG 文件可能很大,建议使用高分辨率 PNG 替代
问题24:HTML 文件过大
症状:生成的 HTML 文件非常大(>10MB)
解决方案:使用 CDN 模式(推荐):output_file("chart.html", mode="cdn"),文件仅几 KB(查看时需要网络)。如需离线使用可改用 mode="inline"(2~5 MB)。对于大数据量场景,建议使用 Bokeh Server 或 AjaxDataSource 按需加载。
调试技巧
通用调试策略
1. 使用 --log-level debug 启动 Bokeh Server
bokeh serve app.py --log-level debug
2. 检查 Bokeh 对象
p = figure()
source = ColumnDataSource(data={'x': [1, 2, 3], 'y': [4, 5, 6]})
r = p.circle('x', 'y', source=source)
print(type(r)) # GlyphRenderer
print(source.column_names) # ['x', 'y']
print(len(source)) # 3
3. 使用浏览器开发者工具(F12 → Console 标签查看 BokehJS 错误)
错误信息速查
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
ValueError: expected an element of ... | 参数类型错误 | 检查参数类型是否匹配 API 文档 |
RuntimeError: Neither firefox/geckodriver nor chrome/chromedriver available | 缺少 WebDriver | 安装 Selenium 和浏览器驱动 |
WebSocket connection to 'ws://...' failed | WebSocket 连接被拒 | 添加 --allow-websocket-origin |
ValueError: ColumnDataSource's columns must be of the same length | 列长度不一致 | 确保所有数据列长度相同 |
RuntimeError: Models must be owned by only a single document | 对象被多个文档引用 | 每个文档创建独立的对象 |
ERROR:tornado.application:Uncaught exception GET /ws | 服务端代码异常 | 检查回调函数中的错误 |
常见调试场景
回调函数中报错但不显示详细信息:
import traceback
def update(attr, old, new):
try:
source.data = compute_data(new)
except Exception as e:
print(f"回调错误: {e}")
traceback.print_exc()
图表显示但交互无反应:检查是否在 Bokeh Server 中运行(Python 回调需要)、回调是否正确绑定、浏览器控制台是否有 JS 错误。
布局不生效:给组件添加背景色辅助调试,确认 layout.children 数量正确。