Bokeh 入门第五步:Glyph 属性向量化与调色板颜色映射实战
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
导读
在 Bokeh 中,Glyph(图形元素)的视觉属性(颜色、大小、透明度等)不仅可以设为固定值,还可以绑定到数据向量,让每个数据点的外观随其数据值动态变化。本文基于 Bokeh 官方入门系列第五篇指南,讲解三种核心技法:直接向量化颜色、向量化颜色与半径、以及使用linear_cmap配合内置调色板完成连续色带映射。读完本文,你将掌握如何用数据驱动的方式一键生成"颜色即数值、大小即数值"的信息可视化,并为后续学习log_cmap、factor_cmap等更复杂的颜色变换打下基础。
一、什么是属性向量化:从固定值到数据驱动
在之前的入门指南(first_steps_4.rst)中,我们通过直接赋值的方式定制图形,例如fill_color="blue"这样把某个颜色固定到整个 Glyph 上。这种写法下,一个 Glyph 的所有数据点共享同一个视觉属性。
向量化(Vectorizing)则完全不同:当我们把一个"长度与数据点数量相同的序列"传给某个视觉属性时,Bokeh 会逐点取值,让每个数据点拥有独立的属性值。这是 Bokeh 中最常用、也最直观的"数据驱动可视化"手段——颜色本身就成为了一维数据的可视化通道。
从源码实现看,Bokeh 的 Glyph 属性实际上都是DataSpec(数据规格),既可以接收标量(如"blue"、15),也可以接收与数据等长的序列或字段名,由底层统一分发到每个数据点。理解这一点后,下面的例子就水到渠成。
二、向量化颜色:让颜色跟随数据变化
2.1 核心思路
不再使用固定的fill_color="blue",而是准备一个与 y 等长的颜色列表,把它传给fill_color参数。Bokeh 会把列表中的每个颜色依次应用到对应的数据点上,实现"每个点的颜色由该点的数据值决定"。
2.2 完整可运行示例
import random from bokeh.plotting import figure, show # 生成数据:x 取 0~25,y 为 0~99 之间的随机整数 x = list(range(0, 26)) y = random.sample(range(0, 100), 26) # 根据 y 的值生成 RGB 十六进制颜色列表: # 红色通道固定 255,绿色通道由 (value * 255) / 100 计算,蓝色通道固定 255 colors = [f"#{255:02x}{int((value * 255) / 100):02x}{255:02x}" for value in y] # 创建绘图对象 p = figure( title="Vectorized colors example", sizing_mode="stretch_width", max_width=500, height=250, ) # 同时添加折线与散点渲染器 p.line(x, y, line_color="blue", line_width=1) p.scatter(x, y, fill_color=colors, line_color="blue", size=15) show(p)对应文件:first_steps_5_vectorize_color.py
2.3 要点解读
- 颜色公式拆解:
f"#{255:02x}{int((value * 255) / 100):02x}{255:02x}"将 y 值(0~100)线性缩放到 0~255 的绿色通道数值,再用02x格式化为两位十六进制字符串,拼成#RRGGBB形式。y 越大,绿色越强,颜色越亮。 - 折线是标量、散点是向量:
p.line(...)仍使用固定的line_color="blue",而p.scatter(..., fill_color=colors)使用向量。二者可以共存于同一图中,互不干扰。 - 数据点颜色与数值一一对应:在本例中,每个圆点的填充色直接反映了该点的 y 值大小——这就是"颜色的向量化"。
如果需要更规范地做"数值 → 颜色"的映射(尤其是数据量较大或需要科学配色时),推荐使用第三节的linear_cmap方式,而非手写十六进制字符串。
三、向量化颜色与大小:半径也可以随数据变化
3.1 核心思路
向量化不只适用于颜色。把同样的原理应用到渲染器的radius(半径)参数上,就能让散点的大小同时携带数据信息,形成"颜色 + 大小"双通道编码。
3.2 完整可运行示例
import numpy as np from bokeh.plotting import figure, show # 生成 1000 个随机数据点 N = 1000 x = np.random.random(size=N) * 100 y = np.random.random(size=N) * 100 # 根据数据生成半径和颜色:半径约为 y 的 2%,颜色随 y 线性变化 radii = y / 100 * 2 colors = [f"#{255:02x}{int((value * 255) / 100):02x}{255:02x}" for value in y] # 创建指定尺寸的绘图对象 p = figure( title="Vectorized colors and radii example", sizing_mode="stretch_width", max_width=500, height=250, ) # 添加圆形渲染器:radius 与 fill_color 均为数据驱动的向量 p.circle( x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color="lightgrey", ) show(p)对应文件:first_steps_5_vectorize_color_and_size.py
3.3 要点解读
radius直接接收数组:radii = y / 100 * 2让每个圆点的半径与其 y 值成正比。注意 Bokeh 中radius使用的是数据坐标系下的长度单位(与size的屏幕像素单位不同),因此随缩放而变化。- 向量化的组合运用:同一个 Glyph 上可以同时向量化多个属性——本例同时向量化了
radius与fill_color,并辅以固定值fill_alpha=0.6、line_color="lightgrey",兼顾数据表达与视觉整洁。 - 透明度缓解重叠:1000 个点大量重叠时,
fill_alpha=0.6让重叠区域的密度得以通过颜色深浅体现出来,是密集散点图的常用技巧。
四、调色板颜色映射:用linear_cmap一键完成"数值 → 颜色"
4.1 Bokeh 内置调色板体系
Bokeh 自带数十种预定义的调色板(palette),覆盖三大家族的经典配色:
- Brewer 系列:来自 ColorBrewer 的定性/顺序/发散配色,如
Blues、Reds、Spectral、Paired等; - D3 系列:来自 D3.js 分类色板,如
Category10、Category20等; - Matplotlib 系列:来自 Matplotlib 的连续色图,如
Viridis、Plasma、Turbo等。
从源码可以看到,这些调色板以字典形式集中定义在 src/bokeh/palettes.py 中,例如第 1412 行的brewer字典(含YlGn、Blues、Spectral、Set1等 35 个命名色板)、第 1454 行的d3字典,以及第 1376 行的Turbo256元组。每个调色板通常还按离散数量分级,例如Turbo支持 3~11 与 256 级。完整清单见参考文档bokeh.palettes(对应源码 src/bokeh/palettes.py)。
4.2linear_cmap函数与四个必需参数
linear_cmap定义在 src/bokeh/transform.py,作用是创建一个客户端侧的LinearColorMapper变换,把某个数据列线性映射到调色板。其必需参数为:
| 参数 | 含义 |
|---|---|
field_name | 要映射颜色的数据字段名(即数据序列) |
palette | 使用的调色板(如Turbo256) |
low | 映射到调色板**最左端(最小颜色)**的最低值 |
high | 映射到调色板**最右端(最大颜色)**的最高值 |
从源码签名还可看到几个可选参数:
low_color(默认None):数据值低于low时使用的颜色;为None时低于low的值被钳制映射到调色板第一个颜色;high_color(默认None):数据值高于high时使用的颜色;为None时高于high的值被钳制映射到调色板最后一个颜色;nan_color(默认"gray"):数据缺失或映射失败时使用的默认颜色。
创建好 mapper 后,把它赋值给渲染器的color参数即可完成映射。
4.3 完整可运行示例
from bokeh.io import show from bokeh.palettes import Turbo256 from bokeh.plotting import figure from bokeh.transform import linear_cmap # 生成数据:y 为 x 的平方 x = list(range(-32, 33)) y = [i**2 for i in x] # 创建线性颜色映射器:字段 "y"、调色板 Turbo256、映射范围为 y 的最小值到最大值 mapper = linear_cmap(field_name="y", palette=Turbo256, low=min(y), high=max(y)) # 创建绘图对象 p = figure(width=500, height=250) # 创建圆形渲染器并应用颜色映射器 p.scatter(x, y, color=mapper, size=10) show(p)对应文件:first_steps_5_color_mapping.py
4.4 要点解读
field_name是列名而非数组:与第一节直接传颜色列表不同,linear_cmap接收的是字段名字符串(这里是"y"),Bokeh 会在内部对数据列逐点执行映射,这正是LinearColorMapper作为客户端侧变换的职责所在。low/high决定色带覆盖范围:本例用min(y)/max(y)让整条色带恰好覆盖数据全范围;你也可以手动指定更小或更大的范围,此时超出范围的数据点会被钳制(clamp)到端点颜色。color=一键搞定填充与描边:p.scatter(..., color=mapper)同时作用于填充色与描边色,是linear_cmap最简洁的用法。- 数据分布与视觉分布:由于 y 是二次函数,中间区域点密集但数值小,两端点稀疏但数值大,使用
Turbo256(256 级渐变)可以清晰呈现数值的连续变化——这正是连续色带相比手工构造颜色列表的优势。
五、延伸:log_cmap、factor_cmap与其他属性向量化
linear_cmap只是 Bokeh 颜色变换家族的一员,同样定义在 src/bokeh/transform.py 中:
log_cmap(源码位置):对数尺度线性映射,参数与linear_cmap完全一致。适用于数值跨越多个数量级的数据(如星系距离、人口密度等),映射前对数值取对数,避免少量大值把色带整体"压扁"。factor_cmap(源码位置):针对分类数据的颜色映射,额外接受factors参数(与调色板一一对应的类别因子序列),将每个类别映射到调色板中的对应颜色;还可通过start/end切片处理多级因子,nan_color处理缺失值。transform(field_name, transform)(源码位置):通用入口,可将任意客户端侧Transform应用到数据列上,是上述各*_cmap的基础设施。
此外,向量化不仅限于颜色与半径。Bokeh 的众多 Glyph 属性(如size、line_width、fill_alpha、line_dash等)都支持向量化赋值,可以组合出极为丰富的数据编码方案。分类数据的颜色/填充纹样/标记映射还可参考factor_hatch、factor_mark(源码位置)。
更系统的颜色映射与数据变换教程,可继续阅读用户指南中的 数据颜色映射与数据变换章节(对应锚点ug_basic_data_color_mapping与ug_basic_data_transforming)。
六、从源码看linear_cmap的底层机制
为了深入理解linear_cmap到底做了什么,我们看它在 src/bokeh/transform.py 中的实现:
def linear_cmap( field_name: str, palette: PaletteLike, low: float, high: float, low_color: ColorType | None = None, high_color: ColorType | None = None, nan_color: ColorType = "gray", ) -> Field: ''' Create a ``DataSpec`` dict that applies a client-side ``LinearColorMapper`` transformation to a ``ColumnDataSource`` column. ... ''' return Field( field_name, LinearColorMapper( palette=palette, low=low, high=high, nan_color=nan_color, low_color=low_color, high_color=high_color, ), )从中可以提炼出三个关键事实:
- 返回类型是
Field:linear_cmap并不直接返回颜色数组,而是返回一个Field——即DataSpec的封装,内部包含字段名与变换器。这意味着它属于"声明式"配置:真正把数据映射成颜色发生在浏览器端(BokehJS)的LinearColorMapper中,而不是在 Python 侧预计算。 - 客户端侧变换(client-side transform):由于映射发生在浏览器内,当数据源动态更新(例如服务端推送新数据)时,颜色会自动重新计算,无需重新发起请求——这是 Bokeh 交互式可视化的核心优势之一。
- 钳制与缺省策略内置于 mapper:
low/high之外的越界值、以及NaN等缺失值,分别由low_color/high_color/nan_color三个参数在映射器内部处理,保证了任何输入数据都能得到确定的输出颜色。
同样的模式也适用于log_cmap(底层为LogColorMapper)与factor_cmap(底层为CategoricalColorMapper),三者共享Field+ 具体ColorMapper的架构,只是映射算法不同。
七、小结:三种数据驱动着色的选型建议
| 需求场景 | 推荐方案 | 对应章节 |
|---|---|---|
| 少量点、快速原型,颜色逻辑简单 | 手动构造颜色列表传入fill_color | 第二节 |
| 大量点、需要同时编码两个维度 | 向量化radius与fill_color | 第三节 |
| 需要专业科学配色、动态数据、健壮的颜色映射 | linear_cmap+ 内置调色板 | 第四节 |
| 数值跨越多个数量级 | log_cmap | 第五节 |
| 类别型数据的颜色区分 | factor_cmap | 第五节 |
向量化是 Bokeh 数据驱动可视化的基石能力:颜色、大小、透明度、线宽等属性都可以是数据的函数。掌握本文的三种写法后,建议继续阅读官方入门系列的后续章节(first_steps_6.rst 及之后),学习布局组合与交互工具,把数据驱动的视觉编码应用到完整的数据应用中。
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考