Bokeh 进阶开发指南:自定义扩展、BokehJS 与第三方生态集成
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
导读
本文基于 Bokeh 官方用户指南的 Advanced usage 章节,系统讲解 Bokeh 面向进阶场景的三条核心路径:通过自定义扩展(Custom Extensions)在 Python 侧声明模型、在 BokehJS 侧实现渲染逻辑,从而把第三方 JavaScript 库接入 Bokeh;直接使用 BokehJS 客户端库进行纯 JavaScript 开发;以及与 hvPlot、Panel、HoloViews、Datashader 等数据科学生态库的组合使用。读完本文,你将掌握__implementation__扩展机制、bokeh init/bokeh build预构建扩展工作流,以及Bokeh.Plotting与Bokeh.Charts两套高层 JS API 的完整用法。
进阶能力总览
Bokeh 核心库自带丰富的内置功能,足以在浏览器中构建复杂的交互式可视化与数据应用。但当某些能力(如过于特定领域的模型,或需要引入第三方库)没有进入核心库时,进阶用户需要借助以下机制扩展 Bokeh:
- 自定义扩展(Custom Extensions):修改既有 Bokeh 模型的行为、把第三方 JavaScript 库连接到 Python、为特定领域创建高度定制化的模型;
- BokehJS 直接开发:跳过 Python,以纯 JavaScript 方式创建交互式图形与应用;
- 第三方生态集成:与 hvPlot、Panel、HoloViews、Datashader 等库协同,各取所长。
本章的完整索引页位于 docs/bokeh/source/docs/user_guide/advanced.rst,下文将按「第三方集成 → 自定义扩展 → BokehJS」三个主题逐一展开。
一、与第三方生态库集成
Bokeh 与大量其他库集成良好,让你能为每个任务选用最合适的工具。集成方向主要有五类。
BokehJS:浏览器端的基石
要在浏览器中显示可视化,Bokeh 会把 Python 数据与事件序列化为 JSON,再交给配套的 JavaScript 库 BokehJS 通信与渲染。BokehJS 也可以作为独立库使用,或与 React、Vue、Angular 等 JavaScript 框架组合使用。关于 BokehJS 的完整开发方式,见本文第三部分。
hvPlot:pandas 风格的极简绘图
hvPlot提供了一套精简 API,让你可以用 pandas 的.plot()函数配合各种数据容器直接在 Bokeh 上绘图。它特别适合交互式数据分析场景,能快速产出常见类型的图。
Panel:构建数据密集型仪表盘
Panel 库提供高层响应式接口,方便在 Bokeh 之上构建数据密集型仪表盘与 Web 应用。它打通了 Jupyter notebook 与 Bokeh server 之间的互操作——你可以在 notebook 中开发、原型验证应用,再部署到服务器上。Panel 同时兼容其他绘图库,能把多个数据科学产物整合进同一个 Bokeh 应用中,并内置多种 JavaScript 模板用于定制应用外观。
HoloViews:声明式高层接口
HoloViews 是独立维护的声明式接口包,专注于 Jupyter notebook 交互,适合快速原型化数据分析图形。例如,用 HoloViews 构建三个联动 Bokeh 图只需一行代码;叠加图层、添加滑块/下拉选择控件、选择工具、标签页等操作也同样简洁。
Datashader:处理数十亿级数据点
默认情况下,Bokeh 会把数据点从 Python 复制到浏览器中的 JavaScript 进程,以提供响应式的本地交互。但浏览器能承载的数据量有限,直接在 JS 中绘制数百万乃至数十亿个点并不可行;且常规渲染技术在大数据量下会因 overplotting(过度绘制)产生误导性图形。
Datashader 是独立的 Python 库,它把再大的数据集也渲染为固定尺寸的栅格图像,忠实呈现底层数据。用 Datashader 构建 Bokeh 图时,缩放、平移操作会在 Python 端动态重新渲染图像,从而让任意规模的数据集都能在标准浏览器中以交互方式展示。Datashader 还与 hvPlot、HoloViews 协作良好,可以在基础图与渲染图之间切换、交错排布 Datashader 图与普通 Bokeh 图等。
二、自定义扩展(Custom Extensions)
Bokeh 自带功能丰富,但某些有用能力未必进入核心库——或因主题过于特殊,或因缺乏资源。自定义扩展可以:
- 修改既有 Bokeh 模型的行为;
- 新增模型以连接第三方 JavaScript 库与 Python;
- 为特定领域创建高度定制的模型。
使用标准发行版即可创建和使用扩展,无需搭建开发环境或从源码构建。这是参与 Bokeh 开发最轻松的入口。需要说明的是,扩展仍是正在积极演进的高级特性,部分环节属于实验性质。
2.1 Bokeh 模型的结构:Python 侧模型
Python 侧的 Bokeh 模型大多是纯声明式类。创建自定义扩展的方式是继承Model(更具体地,可继承UIElement、LayoutDOM、Tool、Glyph等内置基类),并通过特殊类属性声明需要在 JavaScript 侧镜像的属性。所有可用属性类型参见 bokeh/core/properties 与参考指南。
以下示例创建一个为 Slider 定制的读数显示组件:
from bokeh.core.properties import String, Instance from bokeh.models import UIElement, Slider class Custom(UIElement): text = String(default="Custom text") slider = Instance(Slider)这里继承UIElement让扩展可以融入 DOM 布局,并声明了两个属性:
String类型属性text,配置读数显示的文本消息;Instance类型属性slider,持有一个Slider实例,从而在 JavaScript 侧创建与之对应的Slider对象。
2.2 JavaScript 侧模型与视图
Python 侧几乎没有代码,但 JavaScript 侧必须实现模型本身的代码,并在必要时提供对应的视图(View)代码。内置模型正是以这种形式直接打进最终的 BokehJS 脚本中的。
以下是Custom及其CustomView的带注释 TypeScript 实现,完整源码见 examples/advanced/extensions/custom.ts:
import {UIElement, UIElementView} from "models/ui/ui_element" import {Slider} from "models/widgets/sliders/slider" import {div} from "core/dom" import * as p from "core/properties" export class CustomView extends UIElementView { declare model: Custom private content_el: HTMLElement override connect_signals(): void { super.connect_signals() // 监听 slider 的 change 信号,滑块移动时刷新文本 this.connect(this.model.slider.change, () => this._update_text()) } override render(): void { super.render() this.content_el = div({style: { textAlign: "center", fontSize: "1.2em", padding: "2px", color: "#b88d8e", backgroundColor: "#2a3153", }}) this.shadow_el.append(this.content_el) this._update_text() } private _update_text(): void { this.content_el.textContent = `${this.model.text}: ${this.model.slider.value}` } } export namespace Custom { export type Attrs = p.AttrsOf<Props> export type Props = UIElement.Props & { text: p.Property<string> slider: p.Property<Slider> } } export interface Custom extends Custom.Attrs {} export class Custom extends UIElement { declare properties: Custom.Props declare __view_type__: CustomView constructor(attrs?: Partial<Custom.Attrs>) { super(attrs) } static { // 关联视图,属于样板代码 this.prototype.default_view = CustomView // define() 块为 JS 模型声明与 Python 侧一一对应的属性。 // 大多数属性类型都有对应物,例如 Python 的 bokeh.core.properties.String // 对应 JS 的 String;JS 缺少对应类型时可用 p.Any 作为通配类型。 this.define<Custom.Props>(({Str, Ref}) => ({ text: [ Str, "Custom text" ], slider: [ Ref(Slider) ], })) } }要点说明:
connect_signals()中通过this.connect(this.model.slider.change, ...)订阅信号,实现滑块值变化驱动的响应式更新;- 视图默认创建
<div>元素(this.el),可像本例这样修改其内容,也可忽略它改画到 HTML canvas; static {}块中default_view指定视图类,this.define()声明属性及其默认值,与 Python 侧保持一一对应;- 从源码注释可见,
bokeh.core.properties.String对应 JS 的Str,Instance(Slider)对应Ref(Slider)。
2.3 把 Python 与 TypeScript 拼装起来
内置模型在构建过程中会自动把 BokehJS 实现与 Python 模型匹配起来。对自定义模型,Python 类需要提供一个名为__implementation__的类属性,其值为定义客户端模型(及可选视图)的 JavaScript/TypeScript 代码。
假设上例的 TypeScript 代码保存在custom.ts文件中,完整的 Python 类如下:
from bokeh.core.properties import String, Instance from bokeh.models import UIElement, Slider class Custom(UIElement): __implementation__ = "custom.ts" text = String(default="Custom text") slider = Instance(Slider)假设custom.py模块定义了上述类,那么自定义扩展的使用方式与内置模型完全一致。仓库中的完整可运行示例见 examples/advanced/extensions/putting_together.py:
from bokeh.io import show from bokeh.layouts import column from bokeh.models import Slider, UIElement from bokeh.core.properties import Instance, Required, String class Custom(UIElement): __implementation__ = "custom.ts" text = String(default="Custom text") slider = Required(Instance(Slider)) slider = Slider(start=0, end=10, step=0.1, value=0, title="value") custom = Custom(text="Special Slider Display", slider=slider) layout = column(slider, custom) show(layout)渲染出的文档会自动包含实现的 JavaScript 代码。拖动滑块,即可看到特殊样式的标题随滑块值实时更新。注意示例中把slider声明为Required(Instance(Slider)),强制要求传入 slider 实例。
2.4 指定实现语言
当__implementation__的值是单行且以.js或.ts结尾时,Bokeh 会把它解释为文件名:打开该文件,并按扩展名对应的语言编译其内容。
若使用内联实现,则通过JavaScript或TypeScript类显式指定源码语言:
class Custom(Model): __implementation__ = JavaScript(" <JS code here> ")仓库示例中,examples/advanced/extensions/ticking.py 正是用TypeScript(CODE)内联实现了一个自定义TickFormatter:
from bokeh.util.compiler import TypeScript CODE = """ import {TickFormatter} from "models/formatters/tick_formatter" export class MyFormatter extends TickFormatter { // 接受一组数字刻度,返回一组字符串 doFormat(ticks: string[] | number[]) { const formatted = [`${ticks[0]}`] for (let i = 1; i < ticks.length; i++) { formatted.push(`+${(Number(ticks[i]) - Number(ticks[0])).toPrecision(2)}`) } return formatted } } """ class MyFormatter(TickFormatter): __implementation__ = TypeScript(CODE)随后只需p.xaxis.formatter = MyFormatter()即可替换坐标轴刻度格式。
2.5 指定默认值
如果属性有默认值,则必须在 Python 侧和 JavaScript 侧同时提供,且两边的值应当一致。出于效率考虑,Bokeh 只传输用户显式修改过的属性值,因此默认值必须两端对齐。
以默认值为True的布尔属性flag为例,Python 侧:
flag = Bool(default=True)BokehJS 侧(TypeScript 属性声明):
flag: [ Boolean, true ]2.6 引入外部资源
实现自定义模型时可能需要第三方 JavaScript 库或 CSS 资源,可通过自定义模型的__javascript__与__css__类属性提供。把资源 URL 加进 HTML 文档头部后,JS 库将进入全局命名空间,自定义 CSS 也会生效。
以下示例引入 KaTeX(支持 LaTeX 的 JS 库)来创建一个LatexLabel自定义模型:
class LatexLabel(Label): """A subclass of the built-in Bokeh model `Label` that supports rendering LaTeX with the KaTeX typesetting library. """ __javascript__ = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js" __css__ = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css" __implementation__ = """ # do something here """仓库中 examples/advanced/extensions/widget.py 是更完整的实战样例——它用__javascript__同时加载 jQuery 与 ion.rangeSlider,包装出双端滑块扩展IonRangeSlider:
class IonRangeSlider(InputWidget): __implementation__ = "ion_range_slider.ts" __javascript__ = [ "https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js", "https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.4/js/ion.rangeSlider.js", ] disable = Bool(default=True, help="Enable or disable the slider.") grid = Bool(default=True, help="Show or hide the grid beneath the slider.") start = Float(default=0, help="The minimum allowable value.") end = Float(default=1, help="The maximum allowable value.") range = Tuple(Float, Float, help="The start and end values for the range.") step = Float(default=0.1, help="The step between consecutive values.")该扩展通过js_on_change('range', callback)与自定义CustomJS回调联动,实现拖拽范围滑块即重算幂函数曲线的交互效果。
2.7 与 Bokeh server 集成
自定义扩展集成进 Bokeh server 无需任何额外工作。与独立文档一样,渲染出的应用会自动包含 JavaScript 实现;同时,Bokeh 模型属性的标准同步机制对自定义扩展透明生效,与内置模型完全一致。
2.8 扩展示例速览
进阶用户经常需要研读 bokehjs/src/lib/models 下基类的源码才能继续深入。仓库提供了四个方向的基础示例:
| 示例 | 文件 | 技术要点 |
|---|---|---|
| 坐标轴刻度 | examples/advanced/extensions/ticking.py | 继承内置TickFormatter子类化,用TypeScript内联实现自定义刻度格式 |
| 全新工具 | examples/advanced/extensions/tool.py | 继承Tool创建可在画布上拖拽绘图的DrawTool,通过_pan事件把屏幕坐标反算为数据坐标写入ColumnDataSource |
| 包装第三方库 | examples/advanced/extensions/wrapping.py | 继承LayoutDOM包装 vis.js 的 Graph3d,把 Bokeh 数据源映射为 vis.js DataSet 并监听变更 |
| 扩展控件 | examples/advanced/extensions/widget.py | 继承InputWidget引入 ion.rangeSlider,通过__javascript__加载外部资源 |
以 examples/advanced/extensions/tool.py 为例,自定义工具需要同时提供 View(处理事件)与 Model(声明属性):
from bokeh.core.properties import Instance from bokeh.models import ColumnDataSource, Tool from bokeh.plotting import figure, show from bokeh.util.compiler import TypeScript CODE = """ import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool" import {ColumnDataSource} from "models/sources/column_data_source" import {PanEvent} from "core/ui_events" import * as p from "core/properties" export class DrawToolView extends GestureToolView { declare model: DrawTool _pan_start(_e: PanEvent): void { this.model.source.data = {x: [], y: []} } _pan(e: PanEvent): void { const {frame} = this.plot_view const {sx, sy} = e if (!frame.bbox.contains(sx, sy)) return const x = frame.x_scale.invert(sx) const y = frame.y_scale.invert(sy) const {source} = this.model source.get_array("x").push(x) source.get_array("y").push(y) source.change.emit() } _pan_end(_e: PanEvent): void {} } export namespace DrawTool { export type Attrs = p.AttrsOf<Props> export type Props = GestureTool.Props & { source: p.Property<ColumnDataSource> } } export interface DrawTool extends DrawTool.Attrs {} export class DrawTool extends GestureTool { declare properties: DrawTool.Props declare __view_type__: DrawToolView tool_name = "Draw Tool" tool_icon = "bk-tool-icon-lasso-select" event_type = "pan" as "pan" default_order = 12 static { this.prototype.default_view = DrawToolView this.define<DrawTool.Props>(({Ref}) => ({ source: [ Ref(ColumnDataSource) ], })) } } """ class DrawTool(Tool): __implementation__ = TypeScript(CODE) source = Instance(ColumnDataSource) source = ColumnDataSource(data=dict(x=[], y=[])) plot = figure(x_range=(0,10), y_range=(0,10), title="Click and drag to draw", background_fill_color="#efefef", tools="") plot.add_tools(DrawTool(source=source)) plot.line('x', 'y', line_width=3, source=source) show(plot)可见自定义工具只需实现_pan_start/_pan/_pan_end事件钩子,并通过frame.x_scale.invert(sx)等 API 完成屏幕坐标到数据坐标的转换。
2.9 预构建扩展(Pre-built Extensions)
前面介绍的简单内联扩展适合临时性扩展,但在严肃开发中不够方便——例如package.json、tsconfig.json这类隐式配置文件的存在,使得 IDE 无法充分发挥 TypeScript/JavaScript 编写时的补全与检查能力。
预构建扩展解决了这个问题。工作流如下:
- 运行
bokeh init创建全部必要文件,包括bokeh.ext.json、package.json和tsconfig.json; - 运行
bokeh init --interactive可逐步交互式创建并定制扩展; - 运行
bokeh build构建扩展:必要时执行npm install、编译 TypeScript、转译 JavaScript、解析模块并链接成可分发的 bundle; - 若构建缓存引发问题,可用
bokeh build --rebuild从零重建扩展(Bokeh 会缓存编译产物以提升性能)。
三、BokehJS:纯 JavaScript 开发
BokehJS 是客户端库,负责绘图、渲染与事件处理。Bokeh Python 库(以及 R、Scala、Julia 等语言的库)提供了与 BokehJS 的高层交互,让你无需关心 JavaScript 与 Web 开发。但 BokehJS 也有自己的 API,支持纯 JavaScript 开发;自定义扩展模型通常也需要直接与 BokehJS 交互。注意:BokehJS API 仍在开发中,未来版本可能发生变化。
3.1 获取 BokehJS
BokehJS 可通过 CDN 与npm获取,安装细节见安装文档的 BokehJS 章节(对应仓库安装说明见 README.md 与 bokehjs/package.json)。
3.2 低级模型:与 Python 模型一一对应
通常,图与应用的底层模型(坐标轴、网格、字形、控件等)与 Python 侧模型完全一致,因此 Python 参考指南也是 BokehJS 模型的首要参考。Python 库按层级组织,而 JavaScript 模型全部位于扁平的Bokeh模块中——任何 PythonClassName通常都能以Bokeh.ClassName形式在 JavaScript 中使用。完整模型清单见 bokehjs/src/lib/api/models.ts。
在 JavaScript 中创建模型时,把传给 Python 对象初始化器的关键字参数组织为一个 JS 对象。以初始化Range1d为例:
- Python:
xdr = Range1d(start=-0.5, end=20.5)- JavaScript:
const xdr = new Bokeh.Range1d({ start: -0.5, end: 20.5 });这种模式适用于所有类似场景。模型创建后,属性设置方式在两种语言中也完全一致,例如xdr.end = 30在 Python 与 JavaScript 中都会把该Range1d的end设为 30。
以下是从零构建带坐标轴、网格与线字形的图示例,与 examples/models 中的样例对比可见,这一层面的 Python 与 JavaScript 代码几乎一致。完整文件见 examples/advanced/bokehjs/simple_line.js:
// 创建数据与 ColumnDataSource const x = Bokeh.LinAlg.linspace(-0.5, 20.5, 10); const y = x.map(function (v) { return v * 0.5 + 3.0; }); const source = new Bokeh.ColumnDataSource({ data: { x: x, y: y } }); // 创建坐标范围 const xdr = new Bokeh.Range1d({ start: -0.5, end: 20.5 }); const ydr = new Bokeh.Range1d({ start: -0.5, end: 20.5 }); // 创建图 const plot = new Bokeh.Plot({ title: "BokehJS Plot", x_range: xdr, y_range: ydr, width: 400, height: 400, background_fill_color: "#F2F2F7" }); // 添加坐标轴 const xaxis = new Bokeh.LinearAxis({ axis_line_color: null }); const yaxis = new Bokeh.LinearAxis({ axis_line_color: null }); plot.add_layout(xaxis, "below"); plot.add_layout(yaxis, "left"); // 添加网格 const xgrid = new Bokeh.Grid({ ticker: xaxis.ticker, dimension: 0 }); const ygrid = new Bokeh.Grid({ ticker: yaxis.ticker, dimension: 1 }); plot.add_layout(xgrid); plot.add_layout(ygrid); // 添加 Line 字形 const line = new Bokeh.Line({ x: { field: "x" }, y: { field: "y" }, line_color: "#666699", line_width: 2 }); plot.add_glyph(line, source); Bokeh.Plotting.show(plot);3.3 高层接口
与 Python 库类似,BokehJS 也提供多个高层接口,用于操作与组合低级模型对象,主要包括Bokeh.Plotting与Bokeh.Charts。自版本0.12.2起,这两套 API 组成bokeh-api.js文件中的 BokehJS API——除了bokeh.js,还需要额外导入bokeh-api.js才能启用它们。
Bokeh.Plotting:Python plotting 接口的移植
JavaScript 的Bokeh.PlottingAPI 是 Python 侧bokeh.plotting接口的移植,因此用户指南基础绘图章节的内容同样可作为参考。以下示例与 Python 的 examples/basic/scatters/color_scatter.py 非常相似:
const plt = Bokeh.Plotting; // 准备数据 const M = 100; const xx = []; const yy = []; const colors = []; const radii = []; for (let y = 0; y <= M; y += 4) { for (let x = 0; x <= M; x += 4) { xx.push(x); yy.push(y); colors.push(plt.color([50+2*x, 30+2*y, 150])); radii.push(Math.random() * 1.5); } } // 创建数据源 const source = new Bokeh.ColumnDataSource({ data: { x: xx, y: yy, radius: radii, colors: colors } }); // 创建图并添加工具 const tools = "pan,crosshair,wheel_zoom,box_zoom,reset,save"; const p = plt.figure({ title: "Colorful Scatter", tools: tools }); // 调用 circle 字形方法添加圆形 const circles = p.circle({ field: "x" }, { field: "y" }, {field: "radius"}, { source: source, fill_color: { field: "colors" }, fill_alpha: 0.6, line_color: null, }); // 显示图 plt.show(p);Bokeh.Charts:BokehJS 专属高层图表
Bokeh.Charts是 BokehJS 独有的高层图表接口,支持两种图表:pie与bar。其类型定义与实现见 bokehjs/src/lib/api/charts.ts,导出函数为pie(data, opts)与bar(data, opts)。
Bokeh.Charts.pie
Bokeh.Charts.pie(data, { options })其中data是含labels与values键的 JavaScript 对象,options可选键如下:
| 选项 | 类型 | 说明 |
|---|---|---|
width | number | 图表宽度(像素) |
height | number | 图表高度(像素) |
inner_radius | number | 扇区内半径(像素) |
outer_radius | number | 扇区外半径(像素) |
start_angle | number | 扇区起始角(弧度) |
end_angle | number | 扇区结束角(弧度) |
center | [number, number] | 饼图中心的(x, y)位置(像素) |
palette | Palette | Array<Color> | 命名调色板或颜色列表,用于对数值着色 |
slice_labels | "labels" | "values" | "percentages" | tooltip 显示内容 |
从 bokehjs/src/lib/api/charts.ts 的源码可以看出实现细节:pie内部只取values > 0的扇区,默认start_angle为 0、end_angle为start_angle + 2π,inner_radius默认 0、outer_radius默认 1,调色板默认取"Spectral11",并按value / total归一化后累计计算各扇区角度。默认情况下,pie图自动添加 tooltip 与 hover 策略。
const plt = Bokeh.Plotting; const pie_data = { labels: ['Work', 'Eat', 'Commute', 'Sport', 'Watch TV', 'Sleep'], values: [8, 2, 2, 4, 0, 8], }; const p1 = Bokeh.Charts.pie(pie_data); const p2 = Bokeh.Charts.pie(pie_data, { inner_radius: 0.2, start_angle: Math.PI / 2 }); const p3 = Bokeh.Charts.pie(pie_data, { inner_radius: 0.2, start_angle: Math.PI / 6, end_angle: 5 * Math.PI / 6 }); const p4 = Bokeh.Charts.pie(pie_data, { inner_radius: 0.2, palette: "Oranges9", slice_labels: "percentages" }); // 加入文档并显示 const doc = new Bokeh.Document(); doc.add_root(plt.gridplot( [[p1, p2], [p3, p4]], {width: 250, height: 250})); Bokeh.embed.add_document_standalone(doc, document.currentScript.parentElement);Bokeh.Charts.bar
Bokeh.Charts.bar(data, { options })其中data是数组,每个元素代表数据表的一行,首行是列标题。例如各地区各年份的销售数据:
const data = [ ['Region', 'Year', 'Sales'], ['East', 2015, 23000 ], ['East', 2016, 35000 ], ['West', 2015, 16000 ], ['West', 2016, 34000 ], ['North', 2016, 12000 ], ];与pie类似,options是对象,可选键如下:
| 选项 | 类型 | 说明 |
|---|---|---|
width | number | 图表宽度(像素) |
height | number | 图表高度(像素) |
stacked | boolean | 条形是否堆叠 |
orientation | "horizontal" | "vertical" | 条形方向 |
bar_width | number | 每条宽度(像素) |
palette | Palette | Array<Color> | 命名调色板或颜色列表 |
axis_number_format | string | 坐标轴刻度的格式字符串 |
默认情况下,bar图同样自动添加 tooltip 与 hover 策略。示例:
const plt = Bokeh.Plotting; const bar_data = [ ['City', '2010 Population', '2000 Population'], ['NYC', 8175000, 8008000], ['LA', 3792000, 3694000], ['Chicago', 2695000, 2896000], ['Houston', 2099000, 1953000], ['Philadelphia', 1526000, 1517000], ]; const p1 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a" }); const p2 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a", stacked: true }); const p3 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a", orientation: "vertical" }); const p4 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a", orientation: "vertical", stacked: true }); plt.show(plt.gridplot([[p1, p2], [p3, p4]], {width: 350, height: 350}));3.4 最小示例:导入、创建与修改图
以下最小示例展示如何导入库、创建图并动态修改数据:
// 创建数据源 const source = new Bokeh.ColumnDataSource({ data: { x: [], y: [] } }); // 创建带工具的图 const plot = Bokeh.Plotting.figure({ title: 'Example of random data', tools: "pan,wheel_zoom,box_zoom,reset,save", height: 300, width: 300 }); // 从数据源添加一条线 plot.line({ field: "x" }, { field: "y" }, { source: source, line_width: 2 }); // 显示图,追加到当前章节末尾 Bokeh.Plotting.show(plot); function addPoint() { // 添加数据——所有字段长度必须一致 source.data.x.push(Math.random()) source.data.y.push(Math.random()) // 用本地变更更新数据源 source.change.emit() } const addDataButton = document.createElement("Button"); addDataButton.appendChild(document.createTextNode("Some data.")); document.currentScript.parentElement.appendChild(addDataButton); addDataButton.addEventListener("click", addPoint); addPoint(); addPoint();这个示例揭示了 BokehJS 的核心数据流:先修改source.data数组,再调用source.change.emit()广播变更事件,绘图才会刷新。
四、实践建议与资料入口
- 自定义扩展的四个方向性示例(刻度、工具、包装第三方库、扩展控件)集中在 examples/advanced/extensions 目录,每个示例同时给出 Python 模型与 TypeScript 实现,是学习扩展机制的最佳起点。
- 若需深入基类细节,BokehJS 模型源码位于 bokehjs/src/lib/models,高层 API 清单见 bokehjs/src/lib/api/models.ts,Charts 实现见 bokehjs/src/lib/api/charts.ts。
- BokehJS 纯 JS 示例见 examples/advanced/bokehjs/simple_line.js,其余低级模型示例位于 examples/models。
- 官方参考指南(reference guide)中的 bokeh.core.properties 是属性类型(
String、Instance、Bool、Float、Tuple、Required等)的权威清单,编写自定义模型前建议先通读。
综合来看,Bokeh 的进阶能力遵循「Python 声明 + BokehJS 实现 + 生态协同」的分层设计:数据科学场景优先组合 hvPlot、Panel、HoloViews、Datashader 快速交付;需要新交互或接入第三方 JS 库时,用__implementation__或bokeh init/bokeh build构建自定义扩展;而追求极致控制时,可直接用 BokehJS 的Plotting与ChartsAPI 编写纯前端可视化。
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考