Bokeh 与 Jupyter 深度集成指南:从 Notebook 内联输出到 JupyterHub 与 IPyWidgets
2026/9/14 18:22:49 网站建设 项目流程

Bokeh 与 Jupyter 深度集成指南:从 Notebook 内联输出到 JupyterHub 与 IPyWidgets

【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh

本指南基于 Bokeh 官方用户手册 jupyter.rst 展开,系统讲解 Bokeh 与 Jupyter 生态(Classic Notebook、JupyterLab、JupyterHub、Reveal.js 幻灯片、IPyWidgets)的集成方案。读完本文,你将掌握output_notebook/show的内联绘图、push_notebook的原地更新、Bokeh Server 应用在 Notebook 中的嵌入与代理配置,以及把 IPyWidgets 直接搬进 Bokeh 应用的方法,并理解其底层实现原理。

概述:为什么把 Bokeh 放进 Notebook

Jupyter Notebook 是一类"可计算文档",常用于探索性分析、数据处理、教学与演示:Notebook 由一系列输入单元格(input cells)组成,每个单元格可独立执行并立即展示输出。除了经典的ClassicNotebook,还存在更新的JupyterLab项目。Bokeh 对两者都提供完整支持,既可以嵌入独立(standalone)内容(无需 Bokeh Server,纯前端交互),也可以嵌入Bokeh Server 应用(绘图事件、内置控件直接与 Python 回调代码相连)。

这两种内容形态对应 Bokeh 输出体系中的两类目标:独立内容由 bokeh.io 的output_notebook/show驱动;Server 应用则经由 src/bokeh/io/notebook.py 中的 notebook hook 机制嵌入。下文先讲最常用的独立内容内联输出。

在 Notebook 中内联输出独立绘图

Classic Notebook:一行函数切换输出目标

在经典 Jupyter Notebook 中内联显示 Bokeh 绘图,只需使用 bokeh.io 的output_notebook函数,替代(或叠加)output_file即可,无需任何其他修改:

from bokeh.io import output_notebook, show from bokeh.plotting import figure output_notebook() # 之后 show 的输出进入 Notebook 输出单元格 p = figure() p.scatter([1, 2, 3, 4, 5], [6, 7, 2, 4, 5]) show(p)

调用show后,图形会立即显示在下一个 Notebook 输出单元格中。output_fileoutput_notebook可以同时生效——从 output.py 的 docstring 可知,文件输出与 Notebook 输出互不排斥,show会同时向两者写入。

底层实现上,output_notebook会调用run_notebook_hook(notebook_type, "load", ...)(见 output.py),通过load_notebook向单元格发布加载 BokehJS 的 HTML/JS 资源;show则通过 notebook hook 的show_doc动作(对应 notebook.py 的show_doc函数)把绘图序列化为 HTMLdiv与执行脚本,发布为text/htmlapplication/javascript两种 MIME 类型。

在一个单元格中显示多个绘图:只需在同一个输入单元格里多次调用show,绘图会按调用顺序依次排列显示(源码中 show 与output_notebook的 docstring 均明确支持该用法)。

提示:output_notebook还支持verbose(显示详细 BokehJS 加载横幅)、hide_banner(隐藏横幅)、load_timeout(加载超时毫秒数,默认 5000)等参数,详见 output.py。

JupyterLab:安装 jupyter_bokeh 扩展

要在 JupyterLab 中使用 Bokeh,至少需要 JupyterLab 3.0,并安装jupyter_bokeh扩展。安装方式二选一:

# 使用 conda(需先安装 Anaconda 或 Miniconda) conda install jupyter_bokeh # 或使用 pip pip install jupyter_bokeh

安装完成后,JupyterLab 中的用法与 Classic Notebook 完全一致(仍走output_notebook+show)。若你使用 JupyterLab 3.0 之前的旧版本,安装jupyter_bokeh的详细说明请参见 jupyter_bokeh 官方仓库的 README。

Notebook 幻灯片(Reveal.js)中的注意事项

你可以用 Notebook 配合 Reveal.js 从单元格生成幻灯片,独立(非 Server)Bokeh 绘图可以包含在幻灯片中,但需注意一个关键陷阱:包含output_notebook调用的单元格不能被标记为"skip"(跳过)。因为该单元格渲染出的输出负责加载 BokehJS 库——如果这个单元格被跳过,BokehJS 不会加载,所有 Bokeh 绘图都将无法显示。若想隐藏该单元格,应将其幻灯片类型设为"notes"(备注),而不是 "skip"。

使用 notebook handle 原地更新绘图

基本原理:comms 通道与事件收集

"Notebook handle"(Notebook 句柄)允许你在不重新加载、不重新执行绘图所在单元格的情况下更新已显示的图形。其核心机制是 Jupyter 的comms通信通道,与 CommsHandle 的实现直接对应:

  • 当你以notebook_handle=True调用show时,show_doc会创建一个CommsHandle,其内部持有一份与curdoc()模型 ID 完全一致的"副本文档"(cell_doc),并让该文档处于持续hold()状态——原文档上的所有变更事件会被触发并收集起来,直到调用push_notebook时才被处理清空;
  • push_notebook从 handle 中取出累积的DocumentPatchedEvent,打包为 PATCH-DOC 协议消息,通过handle.comms.send(...)发送给前端,实现原地更新(见 push_notebook)。

注意:Notebook handle 功能目前仅在 Classic Jupyter Notebook 中受支持,尚未在 JupyterLab 与 Zeppelin 中实现。

使用步骤

首先引入标准函数与push_notebook

from bokeh.io import output_notebook, push_notebook, show from bokeh.plotting import figure output_notebook()

创建绘图并以notebook_handle=True调用show,保存返回的 handle:

p = figure() p.scatter([1, 2, 3], [4, 6, 5]) handle = show(p, notebook_handle=True)

此时输出单元格会显示该 handle 的表示形式(如<Bokeh Notebook handle for In[2]>,由CommsHandle._repr_html_生成,notebook.py)。

之后修改绘图属性或数据源,再调用push_notebook传入 handle,即可原地更新——原输出单元格会直接变化,而无需重新执行该单元格

p.title.text = "New Title" # 修改标题 p.renderers[0].glyph.fill_color = "white" # 修改字形填充色 push_notebook(handle=handle) # 把累积的变更推送到已显示的图形

从 push_notebook 的 docstring 可以看出,凡是自上次push_notebook(或原始show)以来的属性更新、数据源值变化等,都会一次性应用到先前渲染的输出单元格中。仓库提供了四个可直接运行的示例 Notebook:

  • Basic Usage.ipynb —— 基础用法
  • Continuous Updating.ipynb —— 持续更新
  • Jupyter Interactors.ipynb —— 配合 Jupyter interactors
  • Numba Image Example.ipynb —— Numba 图像示例

与 Jupyter interactors 结合

你可以用 Notebook 控件(即 ipywidgets 的interactors)来更新 Bokeh 绘图,关键仍是push_notebook:interactor 的更新回调函数根据控件值修改绘图属性后调用push_notebook完成推送。完整的交互式示例见上述 Jupyter Interactors.ipynb。

在 Notebook 中嵌入 Bokeh Server 应用

除了独立内容,你还可以把完整的 Bokeh Server 应用嵌入 Notebook:绘图事件与 Bokeh 内置控件直接连接 Python 回调代码。关于 Bokeh Server 应用的通用知识参见用户手册的 Server 章节;一个完整的嵌入示例 Notebook 位于 notebook_embed.ipynb。

信任 Notebook(Trust Notebook)

取决于 Notebook 版本,你可能需要在关闭并重新打开 Notebook 后,对 Notebook 执行**信任(trust)**操作,Bokeh 绘图才能重新渲染。Trust Notebook选项通常位于File菜单下。这是因为未信任的 Notebook 不会执行其中的 JavaScript/HTML 输出。

为什么 JupyterHub 下需要额外配置

当 Notebook 运行在你自己的 JupyterHub 实例上时,嵌入 Bokeh Server 应用需要额外步骤来打通"客户端浏览器 ↔ Bokeh Server"的网络连接。原因在于:浏览器需要直连 Bokeh Server 监听的端口,但 JupyterHub 在浏览器与 JupyterLab 容器之间充当反向代理,导致浏览器无法直接访问该端口。

Bokeh 的解决方案是提供notebook_url参数,它可以接收一个可调用对象(callable),根据整数端口计算最终 URL。更进一步,如果 JupyterHub 管理员设置了环境变量JUPYTER_BOKEH_EXTERNAL_URLnotebook_url的定义过程将完全自动化,无需再手动指定——这意味着同一个 Notebook 既能在 JupyterHub 上运行,也能在独立 JupyterLab 会话中原样运行,无需任何修改。

从源码可以印证这一机制:show_app 在启动内嵌 Server 前会调用_update_notebook_url_from_env(notebook.py):若环境变量JUPYTER_BOKEH_EXTERNAL_URL已设置,则忽略(并警告)用户传入的notebook_url,改用内置的_remote_jupyter_proxy_url(notebook.py)——该函数与文档中推荐的参考实现逻辑一致,用urllib.parse.urljoin拼接外部 Hub 地址、JUPYTERHUB_SERVICE_PREFIXproxy/<port>路径,使浏览器能穿越 JupyterHub 代理回到当前 Notebook 会话。同时,Server 以allow_websocket_origin=[origin]启动,只允许来自该 origin 的 WebSocket 连接。

必需依赖:jupyter-server-proxy

先完成上文所有 JupyterLab(而非 JupyterHub)的安装步骤,然后继续安装jupyter-server-proxy包并启用其服务端扩展:

pip install jupyter-server-proxy && jupyter server extension enable --py jupyter-server-proxy

如果使用 JupyterLab,还需安装对应的扩展,可通过 GUI 安装,或用命令:

jupyter labextension install @jupyterlab/server-proxy

JupyterHub 管理员:环境变量一键配置

作为 JupyterHub 管理员,可以通过在 Notebook 环境中设置环境变量,让 Bokeh 自动工作、无需改动 Notebook:

export JUPYTER_BOKEH_EXTERNAL_URL="https//our-hub.science.edu"

通常这在 JupyterHub Helm chart 配置 YAML 中完成:

hub: single_user: extraEnv: JUPYTER_BOKEH_EXTERNAL_URL="https://our-public-hub-name.edu"

其净效果是:下一节介绍的技术会被 Bokeh 自动采用,无需任何额外操作。

JupyterHub 用户:自定义 notebook_url 回调

对于未设置JUPYTER_BOKEH_EXTERNAL_URL的 Hub,你需要定义一个辅助函数来生成浏览器连接 Bokeh Server 的 URL。以下是参考实现(需根据你的 JupyterHub 安装地址修改代码,或把 Hub 地址赋值给环境变量EXTERNAL_URL;未设置时 JupyterHub 默认使用JUPYTERHUB_SERVICE_PREFIX):

import os import urllib.parse def remote_jupyter_proxy_url(port): """ Callable to configure Bokeh's show method when a proxy must be configured. If port is None we're asking about the URL for the origin header. """ base_url = os.environ['EXTERNAL_URL'] host = urllib.parse.urlparse(base_url).netloc # If port is None we're asking for the URL origin # so return the public hostname. if port is None: return host service_url_path = os.environ['JUPYTERHUB_SERVICE_PREFIX'] proxy_url_path = 'proxy/%d' % port user_url = urllib.parse.urljoin(base_url, service_url_path) full_url = urllib.parse.urljoin(user_url, proxy_url_path) return full_url

将上面定义的函数作为notebook_url关键字参数传给show。Bokeh 会在启动 Server 并创建加载图形的 URL 时调用该函数(回调约定:传入端口生成完整 Server URL,传入None生成 origin URL):

show(obj, notebook_url=remote_jupyter_proxy_url)

此后可能需要重启你的 Server,然后 Bokeh 内容即可正常加载并执行你在 Jupyter 环境中定义的 Python 回调。

IPyWidgets 在 Notebook 之外的使用

掌握了 JupyterLab 与经典 Notebook 环境中的用法后,你还可以借助 Bokeh 的ipywidgets_bokeh扩展,在这些环境之外利用 Jupyter 生态中丰富的控件。安装方式二选一:

conda install -c bokeh ipywidgets_bokeh

pip install ipywidgets_bokeh

该扩展让你在 Bokeh 中使用 IPyWidgets:只需把一个控件包装进IPyWidget模型,再把包装器加入文档或包含进布局即可,无需安装或启用任何其他扩展

实战示例:一个输出到控制台的滑块应用

按照以下步骤构建一个单滑块应用,滑块调整时把角度值打印到控制台:

1. 构造控件并配置观察器:

from ipywidgets import FloatSlider angle = FloatSlider(min=0, max=360, value=0, step=1, description="Angle") def on_change(change): print(f"angle={change['new']} deg") angle.observe(on_change, names="value")

2. 用IPyWidget包装控件以集成到 Bokeh:

from ipywidgets_bokeh import IPyWidget ipywidget = IPyWidget(widget=angle)

3. 把包装器加入 Bokeh 文档:

from bokeh.plotting import curdoc doc = curdoc() doc.add_root(ipywidget)

bokeh serve ipy_slider.py运行该应用(其中ipy_slider.py是应用文件名,Bokeh Server 的通用用法参见用户手册 Server 章节),应用地址为http://localhost:5006/ipy_slider

在此基础上你可以构建更复杂的布局,并集成高级第三方控件,例如ipyleafletipyvolume。仓库中的 examples/output/jupyter/ipywidgets 目录提供了更多示例,其 README 说明这些示例均为 Bokeh Server 应用,使用bokeh server app_name启动后访问http://localhost:5006/app_name

  • ipyleaflet_tiles.py —— IPyLeaflet 与 Bokeh 的 tile renderer 并排使用;
  • ipyvolume_camera.py —— 集成第三方控件,并用 Bokeh 与 IPyWidgets 的控件共同操纵它。

更多示例 Notebook

可以在 bokeh-tutorial 仓库找到更多 Notebook 用法示例:

  1. 在本地克隆仓库:

    git clone https://github.com/bokeh/tutorial.git
  2. 在浏览器中启动这些 Jupyter Notebook。

Bokeh 主仓库也包含一些 Notebook comms 示例,即上文列出的 push_notebook 四个 Notebook(位于 examples/output/jupyter/push_notebook):

  • Basic Usage.ipynb
  • Continuous Updating.ipynb
  • Jupyter Interactors.ipynb
  • Numba Image Example.ipynb

小结

Bokeh 与 Jupyter 的集成覆盖了从"一行代码内联出图"到"生产级 Hub 代理穿透"的完整梯度:

场景关键函数/参数要点
Classic Notebook 内联绘图output_notebook()+show()无需其他配置,多次show可顺序出图
JupyterLabconda install jupyter_bokeh/pip install jupyter_bokeh需 JupyterLab ≥ 3.0
原地更新绘图show(..., notebook_handle=True)+push_notebook(handle)仅 Classic Notebook 支持;基于 comms 通道与事件收集
幻灯片展示单元格类型设为 "notes" 而非 "skip"保证output_notebook单元格执行以加载 BokehJS
JupyterHub 嵌入 Server 应用环境变量JUPYTER_BOKEH_EXTERNAL_URL或自定义notebook_url回调 +jupyter-server-proxy解决反向代理下浏览器无法直连 Bokeh Server 端口的问题
Notebook 之外使用 IPyWidgetsipywidgets_bokeh.IPyWidget包装控件bokeh serve运行,无需其他扩展

理解这些机制背后的实现——notebook hook 体系(notebook.py)、comms 句柄的事件收集与 PATCH-DOC 推送、以及JUPYTER_BOKEH_EXTERNAL_URLnotebook_url的自动覆写——能帮助你在实际项目中更灵活地组合 Bokeh 与 Jupyter 生态,无论是探索性分析、教学演示还是部署到多用户 Hub 环境。

【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询