Vega-Lite — 用 JSON 三段式画复合图
是什么
Vega-Lite 是一种用 JSON 描述图表的小语言,由 University of Washington 的 Interactive Data Lab(IDL,Jeffrey Heer 组)维护。日常类比:像点奶茶——你只需要勾”杯型 / 加料 / 甜度”三栏,店员自动补默认;不像让你手画一杯奶茶。
写一张柱状图就这几行:
{ "data": {"values": [{"a": "A", "b": 28}, {"a": "B", "b": 55}]}, "mark": "bar", "encoding": { "x": {"field": "a", "type": "nominal"}, "y": {"field": "b", "type": "quantitative"} }}三段对应三个槽位:
- mark:用什么图形原语(bar / line / point / area …)
- encoding:哪一列数据映射到哪个视觉通道(x / y / color / size …)
- transform:画之前怎么处理数据(filter / aggregate / bin …)
为什么重要
不理解三段式,下面这些事都没法解释:
- 为什么 Altair / Streamlit / Observable 里”5 行画一张图”比 matplotlib”改 30 行”还快
- 为什么图本身可以复制粘贴分享——一张 spec 就是一段 JSON,谁都能渲染
- 为什么 Wickham 的 ggplot2 在 R 圈封神,而 Python 圈过去十年没有同等地位的工具
- 为什么浏览器里的交互看板(hover、brush、联动)能用同一份 JSON 静态描述
核心要点
三段式之外还有四个复合算子,把单图拼成复合图:
- layer:多张图叠在同一坐标系(柱+折线+均值线)
- concat(hconcat / vconcat):水平 / 垂直拼成多面板
- facet:按一列分组,自动复制图(小多图)
- repeat:对一组列做笛卡尔积,自动生成网格(散点矩阵)
再加 selection(选择器,绑定 brush 或 click),就能写交互联动。
写法上的关键约定:
type必须显式:nominal(类别)/ordinal(有序类别)/quantitative(数值)/temporal(时间)- 同一个 channel(比如
color)在一段 encoding 里出现多次会被默默覆盖,不报错 transform顺序敏感,filter后aggregate与反过来结果不同
实践案例
案例 1:5 行画一张柱状图
{ "data": {"url": "data/cars.json"}, "mark": "bar", "encoding": { "x": {"field": "Origin", "type": "nominal"}, "y": {"aggregate": "count", "type": "quantitative"} }}aggregate: count直接说”按 Origin 分组数行数”,不必先 group by- y 没写
field,等价于”对每行计数”
案例 2:layer 把柱和折线叠到同一图
{ "data": {"url": "data/sales.json"}, "layer": [ {"mark": "bar", "encoding": {"x": {"field": "month"}, "y": {"field": "sales"}}}, {"mark": "line", "encoding": {"x": {"field": "month"}, "y": {"field": "target"}}} ]}两层共享数据和 x 轴,y 各画各的。三段式的力量在这——复合 = 把若干个三段式拼起来。
案例 3:facet 自动小多图
{ "data": {"url": "data/cars.json"}, "mark": "point", "encoding": { "x": {"field": "Horsepower", "type": "quantitative"}, "y": {"field": "MPG", "type": "quantitative"}, "facet": {"field": "Origin", "type": "nominal", "columns": 3} }}一张 spec 自动按 Origin 切成三张小图横排——不用循环、不用拼图。
踩过的坑
-
type 漏写默认当 nominal:把年份 1990/1991/1992 当类别画出来,每年一根独立柱,看起来像离散事件,其实是连续时间。永远显式写
"type": "temporal"或"quantitative"。 -
encoding 重复定义被静默覆盖:同一个 mark 里写两次
color,后一个赢,但没有警告。复制粘贴 layer 时容易触发。 -
transform 顺序敏感:先
filter再aggregate算”过滤后的总和”;先aggregate再filter算”先求总和再过滤”。两种结果都合法但语义完全不同。 -
大数据集卡浏览器:默认前端渲染,超过 5 万点就明显掉帧。需要先用 Vega-Lite 的
bin/aggregate在 spec 里压数据,或者切到 Deck.gl 这类 WebGL 后端。
适用 vs 不适用场景
适用:
- 探索式分析(EDA)——改一行 JSON 换一种视图
- Jupyter / Observable / Streamlit 内嵌图
- 看板需要交互(hover / brush / 联动)但不想写前端
- 团队协作分享图——spec 是 JSON,可粘贴可 diff 可版本控制
不适用:
- 百万级数据点的实时大图 → 用 Deck.gl / regl 这类 WebGL 后端
- 出版级 PDF / 矢量精修 → matplotlib + TikZ 更可控
- 3D / 网络图 / 地理深度交互 → 转 Plotly / Kepler.gl
历史小故事(可跳过)
- 1999 年:Leland Wilkinson 出版《Grammar of Graphics》,提出”图是一门语言而非模板”。纯理论,没人能跑。
- 2005 年:Hadley Wickham 在 R 里实现 ggplot2,工业界第一次感受到”图可以拼”。
- 2014 年:华盛顿大学 IDL 实验室(Heer 组)发布 Vega,完整 JSON DSL,但写一张简单图要 200 行。
- 2017 年:Satyanarayan 等人发表 Vega-Lite——把 Vega 的样板默认掉,只让用户写差异。论文同时给出 layer / concat / facet / repeat 四算子和 selection 模型。
- 2019 起:Altair 把 Vega-Lite 推进 Python 主流圈,成为 ggplot2 在 Python 的对标方案。
学到什么
- 图是语言不是模板——可视化的复杂度来自组合,不是来自堆参数
- 三段式是核心:mark 决定形状、encoding 决定通道、transform 决定输入
- 复合靠四个算子:layer / concat / facet / repeat,覆盖几乎所有”组合需求”
- JSON 作中间表示的价值:可读、可写、可 diff、可程序生成、可跨语言渲染
延伸阅读
- 论文 PDF:Vega-Lite IEEE TVCG 2017(10 页,三段式 + 复合算子讲清楚)
- 上手交互教程:Vega-Lite Tutorials
- Python 包装:Altair 文档(pandas DataFrame 直接喂)
- 在线编辑器:Vega Editor(左边 JSON、右边图,改完即看)
- d3 —— Vega 的底层抽象,更自由但更冗长
- observable-notebook —— Vega-Lite 的天然宿主
关联
- d3 —— D3 用 JS API 描述图,Vega-Lite 用 JSON 描述同一思想
- ggplot2 —— R 里的 grammar of graphics 实现,Vega-Lite 思想同源
- altair —— Python 把 Vega-Lite spec 当目标产物
- observable-notebook —— 浏览器内的 Vega-Lite 主要宿主
反向链接
- altair —— Altair — Python 上的 Vega-Lite 绑定
- antv-g2 —— AntV G2 — 把 Grammar of Graphics 写成 JavaScript
- apexcharts —— ApexCharts — 自带响应式与注解的 SVG 图表库
- d3 —— D3.js — 不是图表库,是写图表库的乐高
- echarts —— Apache ECharts — 给一个 JSON 就能画图的可视化库
- leaflet —— Leaflet — 轻量交互式地图
- mapbox-gl-js —— Mapbox GL JS — 矢量瓦片 + WebGL 客户端渲染地图
- matplotlib —— matplotlib — Python 绘图基石
- observable-plot —— Observable Plot — 你说想看哪两列的关系,库自己画图
- panel —— Panel — 多绘图后端的 Python dashboard
- seaborn —— seaborn — matplotlib 之上的一行统计图
- vega-lite —— Vega-Lite — 用 JSON 三段式画复合图