ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Bokeh 数据源完全指南:从 Python 列表到 ColumnDataSource、视图过滤与浏览器端变换

Bokeh 数据源完全指南:从 Python 列表到 ColumnDataSource、视图过滤与浏览器端变换 Bokeh 数据源完全指南从 Python 列表到 ColumnDataSource、视图过滤与浏览器端变换【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh导读任何可视化的基础都是数据本身。Bokeh 作为面向浏览器的交互式可视化库其数据体系围绕ColumnDataSourceCDS展开——它既是最常用绘图的默认数据载体也是实现多图联动、流式更新、局部修补、客户端过滤等高级能力的关键。本文以 Bokeh 官方用户指南Data sources章节为主体系统讲解从 Python 列表、NumPy 数组到 DataFrame、GroupBy 的多种数据供给方式深入剖析ColumnDataSource的创建、修改、流式追加stream与局部修补patch并完整覆盖浏览器端数据变换linear_cmap / factor_mark / CustomJSTransform与基于CDSView的过滤体系IndexFilter、BooleanFilter、GroupFilter、CustomJSFilter、AjaxDataSource轮询与联动选择。读完本文你将掌握 Bokeh 数据层的完整技术栈能够在实战中按需选择最高效的数据组织与更新方案。一、直接向绘图函数传入数据从 Python 列表说起Bokeh 最直观的数据供给方式是把标准 Python 列表直接传给绘图函数plotting function例如figure.scatterfrom bokeh.plotting import figure x_values [1, 2, 3, 4, 5] y_values [6, 7, 2, 3, 6] p figure() p.scatter(xx_values, yy_values)这里x_values与y_values两个列表分别作为散点的横、纵坐标。Bokeh 的绘图函数如scatter、line、circle都支持这种直接传值的写法适合快速原型验证。更系统的绘图函数用法可参考 basic 用户指南。使用 NumPy 数据由于 Bokeh 底层基于数组运算NumPy 数组可以直接参与绘图写法与 Python 列表几乎一致。下面的例子用 NumPy 生成随机数和余弦曲线import numpy as np from bokeh.plotting import figure x [1, 2, 3, 4, 5] random np.random.standard_normal(5) cosine np.cos(x) p figure() p.scatter(xx, yrandom) p.line(xx, ycosine)注意np.cos(x)会把 Python 列表自动转换为 NumPy 数组说明 Bokeh 的渲染管线天然兼容 NumPy 数据类型无需手动转换。二、ColumnDataSourceBokeh 绘图的核心数据结构ColumnDataSource简称 CDS是大多数 Bokeh 图的核心它为图中的 glyph图形符号提供数据。其源码定义位于 src/bokeh/models/sources.py类注释明确指出它是将列名映射到序列或数组的基础数据结构。当你在渲染器中直接传入 Python 列表或 NumPy 数组时Bokeh 会自动为你隐式创建一个 ColumnDataSource。但显式创建 CDS 能解锁更多高级能力例如多图与控件共享数据多个图、多个 widget 共用同一个 CDS修改一处数据所有关联视图同步更新联动选择linked selection多个渲染器共享同一 CDS 时工具栏选择工具如 BoxSelect、LassoSelect的选中信息也会在渲染器之间共享详见下文联动选择。可以把 ColumnDataSource 理解为一组各带唯一列名的数据序列集合。创建 ColumnDataSource创建最基本的 CDS 需要一个 Python 字典传给data参数字典的key 作为列名字典的value 作为该列的数据值。data中可以是任何非字符串的有序序列包括列表、NumPy 数组以及 pandas Seriesdata {x_values: [1, 2, 3, 4, 5], y_values: [6, 7, 2, 3, 6]} source ColumnDataSource(datadata)重要约束一个 ColumnDataSource 中的所有列必须等长。如果你传入长度不一致的序列Bokeh 将无法创建该 CDS。使用 ColumnDataSource 绘图把 CDS 与渲染器函数配合使用时至少需要传三个参数xCDS 中存放 x 值数据的列名yCDS 中存放 y 值数据的列名source承载上述两列的CDS 对象本身。示例from bokeh.plotting import figure from bokeh.models import ColumnDataSource # 用 dict 作为 ColumnDataSource 的基础 data {x_values: [1, 2, 3, 4, 5], y_values: [6, 7, 2, 3, 6]} # 传入 dict 创建 ColumnDataSource source ColumnDataSource(datadata) # 用 CDS 的两个列创建图 p figure() p.scatter(xx_values, yy_values, sourcesource)关键区别在于此时x和y传入的是字符串列名而数据通过source参数引用这正是 Bokeh 数据与图形解耦的核心机制。修改 ColumnDataSource修改已有 CDS 的数据通过更新其.data属性完成新增一列new_sequence [8, 1, 4, 7, 3] source.data[new_column] new_sequence新增列的长度必须与已有列一致。整体替换全部数据将.data赋值为一个全新的 dictsource.data new_dict整体替换是唯一能改变列长度的方式。任何会改变列长度的更新都必须通过传入新 dict 同时更新所有列无法逐列单独调整长度。三、用 DataFrame 与 GroupBy 作为数据源ColumnDataSource的data参数同样支持 DataFramepandas、Polars、PyArrow或任何 Narwhals 支持的 eager dataframe也支持 pandas 的GroupBy对象source ColumnDataSource(df)使用 DataFrame 时生成 CDS 的列与 DataFrame 的列一一对应列命名遵循以下规则若 DataFrame 有命名索引列CDS 中也会有同名列若索引名为None或没有索引CDS 使用通用名优先用index若该名被占用则用level_0。pandas MultiIndex自动扁平化以 pandasMultiIndex作为 CDS 基础时Bokeh 会先把列与索引扁平化索引生成一个元组列并把 MultiIndex 各层名字用下划线连接列名同样用下划线连接。例如df pd.DataFrame({(a, b): {(A, B): 1, (A, C): 2}, (b, a): {(A, C): 7, (A, B): 8}, (b, b): {(A, D): 9, (A, B): 10}}) cds ColumnDataSource(df)结果会生成名为index的列值为[(A, B), (A, C), (A, D)]以及名为a_b、b_a、b_b的列。该过程仅支持字符串列名。若使用非字符串列名需要先手动扁平化 DataFrame 再传入 CDS。pandas GroupBydescribe() 统计列group df.groupby((colA, ColB)) source ColumnDataSource(group)传入GroupBy对象时CDS 的列对应group.describe()的结果describe()会对所有未分组的原始列生成如mean、count等统计量。结果 DataFrame 具有 MultiIndex 列原始列名 统计量名Bokeh 按上述规则扁平化。例如 DataFrame 含year与mpg两列时传入df.groupby(year)会得到形如mpg_mean的列。注意使用GroupBy对象需要 pandas 0.20.0 及以上版本。四、高效增量更新stream 与 patchstream流式追加数据ColumnDataSource.stream()是向 CDS追加新数据的高效方式。其底层实现在 src/bokeh/models/sources.py核心优势是只把新增数据发送到浏览器而不是重发整个数据集非常适用于实时行情、传感器采集等高频追加场景。stream接受new_data参数一个把列名映射到待追加序列的 dict。你可以从一个空 CDS 开始由数据本身初始化各列。另外还有一个可选参数rollover即保留数据的最大长度当数据超过该上限时Bokeh 会从列头部丢弃旧数据。默认值为None表示数据可无界增长。source ColumnDataSource(datadict(foo[], bar[])) # 所有列的更新长度必须一致 new_data { foo : [10, 20], bar : [100, 200], } source.stream(new_data)流式更新的完整示例可查看 examples/server/app/ohlc。patch局部数据修补ColumnDataSource.patch()是更新数据源局部切片的高效方式。其实现位于 src/bokeh/models/sources.py同样只把变更数据发送到浏览器。patch接受一个 dict把列名映射到表示修补变更的元组列表常用元组形式(index, new_value) # 替换单个值 # 或 (slice, new_values) # 替换多个值完整示例见 examples/server/app/patch_app.py。五、浏览器端数据变换Transform除把数据预先算好放进 CDS 外Bokeh 还支持直接在浏览器中执行部分数据运算。例如在浏览器端动态计算颜色映射可以减少 Python 侧代码若颜色映射所需计算全部在浏览器完成传输的数据量也会更少。本节概述各类 transform 对象。客户端颜色映射linear_cmap / log_cmap / eqhist_cmap颜色映射把一列数值编码为具体颜色。Bokeh 提供三个在浏览器端完成颜色映射的函数bokeh.transform.linear_cmap线性颜色映射bokeh.transform.log_cmap对数颜色映射bokeh.transform.eqhist_cmap直方图均衡化颜色映射。三者用法相似均接受以下参数一个 CDS 列名要映射颜色的数据列一个调色板可以是 Bokeh 预定义调色板也可以是自定义颜色列表颜色映射范围的最小值min或low与最大值max或high。映射逻辑数据列中的数值从min到max线性映射到调色板各颜色上。例如用linear_cmap()配合范围[0, 99]与颜色[red, green, blue]值到颜色的映射如下x 0 : red # 低于下限的值被钳制为 red 0 x 33 : red 33 x 66 : green 66 x 99 : blue 99 x : blue # 高于上限的值被钳制为 blue典型用法fill_colorlinear_cmap(counts, Viridis256, min0, max10)把映射结果赋给绘图对象的颜色属性如fill_color。完整可运行示例见 examples/basic/data/linear_cmap.py其中用 50000 个正态分布随机点做六边形分箱hexbin并以linear_cmap(counts, Viridis256, 0, max(bins.counts))作为hex_tile的填充色。映射函数返回的 dataspec 中内含bokeh.transform信息可以取出结果在别的上下文中复用例如据此创建ColorBarfrom bokeh.models import ColumnDataSource from bokeh.plotting import figure, show from bokeh.transform import linear_cmap x list(range(1, 11)) y list(range(1, 11)) source ColumnDataSource(dict(xx, yy)) p figure(width300, height300, titleLinear color map based on Y) # 引用数据源中的列名 cmap linear_cmap(field_namey, paletteSpectral6, lowmin(y), highmax(y)) r p.scatter(xx, yy, colorcmap, size15, sourcesource) # 从 scatter 渲染器构造 ColorBar color_bar r.construct_color_bar(width10) p.add_layout(color_bar, right) show(p)完整示例见 examples/basic/data/linear_cmap_colorbar.py。映射标记类型factor_mark处理类别型categorical数据时可以为不同类别自动分配不同标记。使用bokeh.transform.factor_mark函数p.scatter(flipper_length_mm, body_mass_g, sourcedata, legend_groupspecies, fill_alpha0.4, size12, markerfactor_mark(species, MARKERS, SPECIES), colorfactor_cmap(species, Category10_3, SPECIES))完整示例 examples/basic/data/transform_markers.py 使用 Palmer 企鹅数据集展示了三种企鹅物种在body_mass与flipper length上的分布factor_mark按species类别分配[hex, circle_x, triangle]三种标记同时用factor_cmap对该列做类别颜色映射。注意factor_mark通常只对scatterglyph 方法有意义因为按标记类型参数化只有散点图才适用。自定义 JavaScriptCustomJSTransform除了内置变换函数你还可以注入自己的 JavaScript 代码使用bokeh.models.transforms.CustomJSTransform。其核心参数v_funcvectorized function的缩写你提供的 JS 代码需要从变量xs读取输入数组并返回一个变换后的 JavaScript 数组。下面示例把原始股价转换为相对于首日收盘价的归一化收益率序列v_func const first xs[0] const norm new Float64Array(xs.length) for (let i 0; i xs.length; i) { norm[i] xs[i] / first } return norm normalize CustomJSTransform(v_funcv_func) plot.line(xaapl_date, ytransform(aapl_close, normalize), line_width2, color#cf3c4d, alpha0.6,legendApple, sourceaapl_source)完整示例 examples/basic/data/transform_customjs.py 中AAPL 与 GOOG 的收盘价分别通过transform()与CustomJSTransform组合绘制在同一张以日期为 x 轴的图上实现了两条可比价的归一化股价曲线。六、基于 CDSView 的数据过滤Bokeh 用视图view概念选取数据子集由CDSView类表示源码见 src/bokeh/models/sources.py其类注释为对 ColumnDataSource 的行级子集视图。使用视图时可以用一个或多个过滤器选择特定数据点而不改变底层数据且视图可以在多个图之间共享。要把过滤后的子集绘制出来只需把CDSView传给任意渲染器方法的view参数。CDSView只有一个属性filter它是Filter模型的实例各类过滤器源码位于 src/bokeh/models/filters.py。下面的例子创建名为view的视图基于 CDSsource使用filter1与filter2两个过滤器的交集再传给scatter渲染器from bokeh.plotting import figure from bokeh.models import ColumnDataSource, CDSView filter1 ... # IndexFilter(), BooleanFilter(), etc. filter2 ... source ColumnDataSource(some_data) view CDSView(filterfilter1 filter2) p figure() p.scatter(xx, yy, sourcesource, viewview)IndexFilter按下标选取IndexFilter是最简单的过滤器其indices属性是一个整数列表表示要绘制的数据下标。示例 examples/basic/data/filter_index.py 中左图绘制全部 5 个点右图通过CDSView(filterIndexFilter([0, 2, 4]))只绘制下标为 0、2、4 的点两图共享同一数据源便于对比。BooleanFilter按布尔掩码选取BooleanFilter通过booleans属性中的True/False列表选取行。示例 examples/basic/data/filter_boolean.py 用列表推导生成掩码bools [True if y_val 2 else False for y_val in source.data[y]] view CDSView(filterBooleanFilter(bools))GroupFilter按类别分组选取GroupFilter是面向类别型数据的过滤器可选取属于特定类别的行。它有两个属性column_name要应用过滤的 CDS 列名group要选取的类别名。示例 examples/basic/data/filter_group.py 中数据集data含类别变量speciesAdelie、Chinstrap、Gentoo三类。第二个图用GroupFilter(column_namespecies, groupAdelie)只显示Adelie类别的点source ColumnDataSource(data) view CDSView(filterGroupFilter(column_namespecies, groupAdelie))CustomJSFilter自定义 JS/TS 过滤器你还可以用 JavaScript 或 TypeScript 编写自定义过滤器通过CustomJSFilter类接入代码以字符串形式传给code参数。代码需要返回一个下标列表或布尔列表来表示过滤后的子集。在代码中可以通过变量source访问与CDSView关联的 ColumnDataSourcecustom_filter CustomJSFilter(code const indices []; // 遍历数据源的每一行判断是否满足某个约束 for (let i 0; i source.get_length(); i){ if (source.data[some_column][i] some_value){ indices.push(true); } else { indices.push(false); } } return indices; )从源码看src/bokeh/models/filters.pyCustomJSFilter还支持args参数用于向 JS 代码传入额外的 Python 对象进一步增强过滤逻辑的灵活性。七、AjaxDataSource无服务端的轮询式数据源流式更新stream与 Bokeh Server 应用配合得非常好但在独立文档standalone document中同样可以借助AjaxDataSource实现类似能力且无需运行 Bokeh server。其类定义位于 src/bokeh/models/sources.py。配置AjaxDataSource需要两个关键信息REST 端点的 URL与轮询间隔。在浏览器中数据源按指定间隔向端点请求数据再用返回数据更新本地数据。本地数据更新有两种模式通过mode参数控制replace默认整体替换本地数据append把新数据追加到已有数据之后可配合max_size限制上限。端点应返回符合标准 CDS 格式的 JSON dict即列名 → 数值数组的映射{ x : [1, 2, 3, ...], y : [9, 3, 2, ...] }如果 REST API 返回的是其他格式可以通过adapter属性提供一个CustomJS回调把响应转换为 Bokeh 格式。除此之外AjaxDataSource的用法与标准ColumnDataSource完全一致# 配置 AjaxDataSourceURL 轮询间隔 source AjaxDataSource(data_urlhttp://some.api.com/data, polling_interval100) # 像普通 ColumnDataSource 一样使用 p.circle(x, y, sourcesource)完整的可运行示例见 examples/basic/data/ajax_source.py该示例用 Flask 起了一个本地 REST 服务端口 5050不断追加带噪声的 sin(x) 数据点前端AjaxDataSource每 100ms 轮询一次/data并通过adapter的CustomJS把{points: [[x, y], ...]}转换为{x: [...], y: [...]}的标准格式同时p.x_range.follow end与p.x_range.follow_interval 10让 x 轴跟随最新数据滚动形成实时流动的散点图。八、联动选择与过滤子集联动联动选择Linked selection如果两个图使用同一个 ColumnDataSource它们之间的选择是共享的在一个图中用工具栏选择工具框选数据另一个图的对应数据也会同步高亮。示例见 examples/interaction/linking/linked_brushing.py。带过滤的联动选择结合 CDSView还可以让两个图基于同一数据但各取不同子集同时仍通过共享的 CDS 保持选择selection与悬停检查hovered inspections的联动。示例 examples/basic/data/linked_brushing_subsets.py 演示了三种行为第二个图是第一个图数据的子集通过CDSView(filterBooleanFilter([True if y 250 or y 100 else False for y in y1]))只保留 y 值大于 250 或小于 100 的点在任意一图用BoxSelect工具框选另一图的对应数据会同步被选中在一个图上悬停某点时另一图中对应的点若存在也会被高亮。九、其他数据类型除了表格型数据Bokeh 还支持网络图数据与地理数据的渲染。关于这两类数据如何组织并传入 Bokeh请参见 用户指南 Topics 章节 中的 Graph 与 Geo 小节。小结Bokeh 的数据体系可以归纳为一条主线底层数据列表 / NumPy / DataFrame / GroupBy→ ColumnDataSource列名映射的序列集合→ 渲染器按列名取数 CDSView行级过滤→ 浏览器端 Transform颜色 / 标记 / 自定义 JS 变换→ 增量更新stream / patch与轮询AjaxDataSource。在实际项目中按数据规模与更新频率选择合适的数据供给与更新策略可以显著减少网络传输量并提升交互流畅度而共享 CDS 带来的联动选择能力则是构建复杂仪表盘时最有价值的设计模式之一。【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表