第四章:数据转换与处理
延伸阅读:本章聚焦“数据如何进入图、如何映射到视觉属性、如何按条件筛选”。如果你更关心图表为什么会卡、何时该降采样、何时该考虑 WebGL 或 Datashader,请继续阅读第九章。
4.1 为什么需要数据转换?
场景:你有100个数据点,想根据数值大小设置颜色。
笨办法:手动计算每个颜色
colors = []
for v in values:
if v < 30:
colors.append('red')
elif v < 70:
colors.append('yellow')
else:
colors.append('green')
Bokeh方式:使用transform
from bokeh.transform import linear_cmap
p.circle('x', 'y', source=source,
fill_color=linear_cmap('value', 'RdYlGn10', low=0, high=100))
4.2 颜色映射
线性映射(linear_cmap):
from bokeh.transform import linear_cmap
from bokeh.palettes import Viridis256
# 连续数值映射到颜色
mapper = linear_cmap('value', Viridis256, low=0, high=100)
p.circle('x', 'y', source=source, fill_color=mapper)
分类映射(factor_cmap):
from bokeh.transform import factor_cmap
# 类别映射到颜色
factors = ['A', 'B', 'C']
mapper = factor_cmap('category', 'Category10', factors)
p.vbar(x='category', top='value', source=source, fill_color=mapper)
字段映射(transform):
from bokeh.transform import transform
# 通用映射函数
mapper = transform('value', LinearColorMapper(palette='Viridis256', low=0, high=100))
4.3 位置变换
dodge - 偏移:
from bokeh.transform import dodge
# 用于分组柱状图
p.vbar(x=dodge('category', -0.2, range=p.x_range), top='value1', source=source)
p.vbar(x=dodge('category', 0.2, range=p.x_range), top='value2', source=source)
jitter - 抖动:
from bokeh.transform import jitter
# 避免点重叠
p.circle(x=jitter('category', width=0.3, range=p.x_range), y='value', source=source)
stack - 堆叠:
from bokeh.transform import stack
# 堆叠柱状图
p.vbar(x='category', top=stack('value1', 'value2', 'value3'), source=source)
4.4 数据过滤器
IndexFilter - 索引过滤:
from bokeh.models import IndexFilter
# 只显示索引0, 2, 4的数据
view = CDSView(filter=IndexFilter([0, 2, 4]))
p.circle('x', 'y', source=source, view=view)
BooleanFilter - 布尔过滤:
from bokeh.models import BooleanFilter
# 只显示值大于50的点
booleans = [v > 50 for v in source.data['value']]
view = CDSView(filter=BooleanFilter(booleans))
p.circle('x', 'y', source=source, view=view)
GroupFilter - 分组过滤:
from bokeh.models import GroupFilter, CDSView
# 只显示category为'A'的数据
view = CDSView(filter=GroupFilter(column_name='category', group='A'))
p.circle('x', 'y', source=source, view=view)
组合过滤器:
from bokeh.models import IntersectionFilter
# 多条件组合
view = CDSView(filter=IntersectionFilter(filters=[
GroupFilter(column_name='category', group='A'),
BooleanFilter([v > 50 for v in source.data['value']])
]))
4.5 自定义表达式
from bokeh.models import CustomJSExpr
# 使用JavaScript表达式计算新列
expr = CustomJSExpr(args=dict(source=source), code='''
return source.data['value'].map(v => v * 2)
''')
source.data['doubled'] = expr
4.6 GroupBy 数据聚合
import pandas as pd
from bokeh.models import ColumnDataSource
# 创建 DataFrame
df = pd.DataFrame({
'category': ['A', 'A', 'B', 'B', 'C'],
'value': [10, 20, 30, 40, 50]
})
# 从 GroupBy 创建 ColumnDataSource
group = df.groupby('category')
source = ColumnDataSource(group)
# source 包含以下列:
# - category_mean, category_std, etc.
4.7 本章与性能优化的边界
到这里,你已经接触了三类非常重要的“数据到视觉”的工具:
- 映射:
linear_cmap()、factor_cmap()、transform()
适合把数值或类别映射到颜色、大小等视觉属性。 - 位置变换:
dodge()、jitter()、stack()
适合在不改原始数据语义的前提下调整显示位置。 - 筛选:
CDSView+ 各类 filter
适合控制“哪些数据被画出来”。
但要注意:这些工具的目标主要是让已有数据更适合可视化表达,不是替代 pandas / NumPy 的通用数据处理流程,也不是完整的性能优化方案。
如果你遇到的是下面这些问题:
- 点太多,浏览器渲染明显变慢
- 每次交互都要传很多数据
- 实时更新时整张图频繁重绘
- 想知道什么时候该降采样、聚合、用
stream()/patch() - 想评估 WebGL 是否真的适合你的场景
请转到第九章:性能优化。
那里会按“先减少数据量,再考虑渲染后端”的顺序系统展开,而不是把 WebGL 当成默认答案。
4.8 本章小结
本章你已经掌握了 Bokeh 中最常见的数据转换思路:
- 用
linear_cmap()、factor_cmap()把数据映射到颜色 - 用
dodge()、jitter()、stack()调整图形位置 - 用
CDSView和过滤器控制显示的数据子集 - 了解哪些转换适合放在 Bokeh 中做,哪些更适合提前在 pandas / NumPy 中处理
如果你准备继续学习,推荐按下面的顺序前进:
4.9 常见坑
坑 1:把 transform 当成通用数据处理工具
linear_cmap()、factor_cmap()、dodge() 这些函数更适合“为了画图而做的转换”。
如果你需要复杂清洗、分组、聚合、时间对齐,通常应该先在 pandas / NumPy 中完成,再交给 Bokeh。
坑 2:过滤条件写对了,但 source 本身的数据就不一致
很多“为什么过滤后图不显示”的问题,根源并不是 filter 本身,而是 ColumnDataSource 中各列长度不一致,或者列名写错。
这类问题请回看第二章中 ColumnDataSource 的基本约束。
坑 3:过早把问题归结为性能
如果当前问题只是“颜色映射不对”或“分类轴位置不对”,不要立刻跳到 WebGL、降采样这类方案。
先确认:
- 数据列名是否正确
low/high是否覆盖了数据范围x_range/y_range是否与变换方式匹配CDSView的过滤条件是否真的命中了数据
坑 4:在 Bokeh 中做了太多业务逻辑
Bokeh 很擅长把数据变成图,但不适合承担整套业务数据处理流水线。
经验上更稳妥的分工是:
- pandas / NumPy:清洗、聚合、衍生字段
- Bokeh:映射、筛选、交互、渲染