0%

Vega-Schema解析

Vega-Schema解析

vegaschema设计以json格式描述数据视觉外观和交互式行为而闻名。 作为业界标杆本文简析Vega-Schema的设计。

规范(Specification)

以下是 Vega 规范的基本概述。完整的规范包括数据刻度标记等属性的相应子集的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A specification outline example.",
"width": 500,
"height": 200,
"padding": 5,
"autosize": "pad",
"signals": [],
"data": [],
"scales": [],
"projections": [],
"axes": [],
"legends": [],
"marks": []
}

配置(Config)

config 对象定义默认视觉值以设置可视化的主题

例如,此 Vega 规范默认包含浅灰色轴网格线:

1
2
3
4
5
6
7
8
9
{
"config": {
"axis": {
"grid": true,
"gridColor": "#dedede"
}
},
...
}

数据集(data)

数据集定义和转换定义要加载的数据以及如何处理这些数据

以下是直接在规范中定义数据的示例:

1
{"name": "table", "values": [12, 23, 47, 6, 52, 19]}

还可以从外部文件(在本例中为 JSON 文件)加载数据:

1
{"name": "points", "url": "data/points.json"}

或者,可以简单地声明数据集的存在。然后,可以在实例化可视化时动态提供数据。有关更多信息,请参阅 View API 文档。

1
{"name": "table"}

最后,可以从现有数据集中提取并应用新的数据转换。在这种情况下,我们通过计算从源数据集中提取的组的聚合统计数据来创建新的数据集 ():"stats"``"table"

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "stats",
"source": "table",
"transform": [
{
"type": "aggregate",
"groupby": ["x"],
"ops": ["average", "sum", "min", "max"],
"fields": ["y", "y", "y", "y"]
}
]
}

变换(Transforms)

转换处理数据流以筛选数据、计算新字段或派生新数据流。转换通常在数据定义的数组中指定。此外,可以在标记定义的数组中使用不筛选或生成新数据对象的转换,以指定编码后转换。transform``transform

以下示例定义了一个新的数据集,其中包含用于筛选值的转换,然后计算堆叠布局(例如,对于堆叠条形图):

1
2
3
4
5
6
7
8
9
10
11
{
"data": [
{
"name": "table",
"transform": [
{ "type": "filter", "expr": "datum.value > 5" },
{ "type": "stack", "field": "value", "groupby": ["category"] }
]
}
]
}

所有转换都需要一个 type 属性,用于指定转换的名称。生成值作为副作用的转换(特别是 binextentcrossfilter 转换)可以包含一个 signal 属性,用于指定要将转换的状态值绑定到的唯一信号名称。

基本变换

  • aggregate - 对数据流进行分组和汇总。
  • bin - 将数值离散为统一的 bin。
  • collect - 收集和排序流中的所有数据对象。
  • countpattern - 计算文本字符串中模式的频率。
  • cross - 执行数据流与自身的叉积。
  • density - 生成从概率分布中提取的值。

触发器(Triggers)

根据 Shift 单击交互更新选定项目的数据集。该信号指示在单击事件期间是否按下 Shift 键,并且该信号引用与单击的标记项对应的数据对象。(请注意,这些示例中未显示信号定义。如果发生 click 事件且未按 Shift 键,则会删除所有选定项。如果在没有 Shift 键的情况下单击标记项,则其数据对象将添加到数据集中。如果在按下 Shift 键时单击标记项,则会切换其数据对象。shift``clicked

1
2
3
4
5
6
7
8
9
10
11
"data": [
...
{
"name": "selected",
"on": [
{"trigger": "!shift", "remove": true},
{"trigger": "!shift && clicked", "insert": "clicked"},
{"trigger": "shift && clicked", "toggle": "clicked"}
]
}
]

将信号引用的标记项的 and 属性设置为当前鼠标位置:fx``fy``dragged

1
2
3
4
5
6
7
8
9
10
11
{
"type": "symbol",
"from": {"data": "nodes"},
"encode": {...},
"on": [
{
"trigger": "dragged",
"modify": "dragged",
"values": "{fx: x(), fy: y()}"
}
]

投影(Projections)

制图投影将 (经度、纬度) 对映射到投影的 (x, y) 坐标。Vega 使用投影来布局经度和纬度坐标是输入数据一部分的地理点(例如地图上的位置),以及使用 GeoJSON 格式表示的地理区域(例如国家/地区和州/省/市/自治区)。

Vega 包括 d3-geo 库提供的所有制图投影以及投影。mollweide

Type Description
albers Albers 等积圆锥投影。这是以美国为中心的 ."conicEqualArea"
albersUsa 以美国为中心的合成图,包含对美国本土 48 个州、夏威夷和阿拉斯加的预测(缩放到真实相对面积的 0.35 倍)。
azimuthalEqualArea 方位等积投影。
azimuthalEquidistant 方位等距投影。
conicConformal 圆锥等角投影。纬线默认为 [30°, 30°] 导致平顶。
conicEqualArea Albers 等积圆锥投影。
conicEquidistant 圆锥等距投影。
equalEarth 平等地球投影
equirectangular 等距柱状投影(板式圆柱投影)类似于直接使用经度、纬度。
gnomonic 球心投影。
identity 标识变换,可用于平移、缩放和剪辑平面几何体。还支持其他布尔 reflectXreflectY 参数。
mercator 球面墨卡托投影。使用默认值,以便将世界投影到一个正方形,裁剪到大约 ±85° 纬度。clipExtent
mollweide 一种等积伪圆柱地图投影,通常用于世界或夜空的全球地图。≥ 5.9
naturalEarth1 自然地球投影是由 Tom Patterson 设计的伪圆柱投影。它既不是等角的,也不是等面积的,但对全世界的小比例尺地图很有吸引力。
orthographic 正交投影。
stereographic 立体投影。
transverseMercator 横向球面墨卡托投影。使用默认值,以便将世界投影到一个正方形,裁剪到大约 ±85° 纬度。clipExtent

缩放/比例尺(scales)

缩放/比例尺-类型

连续-连续

定量比例尺将连续域(数字或日期)映射到连续输出范围(像素位置、大小、颜色)。可用的定量刻度类型值包括 linearlogpowsqrtsymlogtimeutc。除 和 之外的所有定量尺度都使用默认 [0, 1] 和默认单位范围 [0, 1]。time,utc

所有定量刻度都支持颜色值范围,定义为颜色字符串数组或方案规范。如果域包含两个颜色值,则使用顺序色阶。如果域包含三个颜色值,则使用发散色阶。对于较大的域大小,域和范围应具有相同的值数,将应用分段插值。

离散-离散

离散比例尺将值从离散域映射到离散范围。对于 和 scales,范围是通过离散化连续数值范围来确定的。band``point

序数

此示例对颜色编码类别使用序数刻度,最多具有 20 种唯一颜色:

1
2
3
4
5
6
7
8
9
10
{
"scales": [
{
"name": "color",
"type": "ordinal",
"domain": {"data": "table", "field": "category"},
"range": {"scheme": "category20"}
}
]
}
连续-离散

离散化尺度将连续域分解为离散段,然后将每个段中的值映射到范围值。

此示例使用从连续配色方案中采样的颜色对分位数值进行颜色编码:

1
2
3
4
5
6
{
"name": "color",
"scale": "quantile",
"domain": {"data": "table", "field": "value"},
"range": {"scheme": "plasma", "count": 5}
}

给定以下比例定义,

1
2
3
4
5
6
{
"name": "threshold",
"type": "threshold",
"domain": [0, 1],
"range": ["red", "white", "blue"]
}

scale 会将 domain 值映射到颜色字符串,如下所示:

1
2
3
4
5
-1   => "red"
0 => "white"
0.5 => "white"
1.0 => "blue"
1000 => "blue"

配色方案(Color Schemes)

颜色方案为离散和连续颜色编码提供了一组命名的调色板。Vega 提供了一系列基于感知的配色方案,其中许多配色方案最初由 Cynthia BrewerColorBrewer 项目或 Tableau Software 的 Maureen Stone 创建。

离散配色方案可以直接用于具有离散(或离散化)属性域的尺度,例如序数量化分位数尺度。连续颜色方案可以直接与连续比例(例如线性对数sqrt 比例)一起使用,并且 - 通过指定 scheme 属性 - 也可用于生成离散颜色方案。count

轴(Axes)

以下示例说明如何设置自定义颜色、粗细、文本角度和字体。encoding 块还使图例标签响应输入事件,并在鼠标悬停时更改文本颜色。labels

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
"axes": [
{
"orient": "bottom",
"scale": "x",
"title": "X-Axis",
"encode": {
"ticks": {
"update": {
"stroke": {"value": "steelblue"}
}
},
"labels": {
"interactive": true,
"update": {
"text": {"signal": "format(datum.value, '+,')"},
"fill": {"value": "steelblue"},
"angle": {"value": 50},
"fontSize": {"value": 14},
"align": {"value": "left"},
"baseline": {"value": "middle"},
"dx": {"value": 3}
},
"hover": {
"fill": {"value": "firebrick"}
}
},
"title": {
"update": {
"fontSize": {"value": 16}
}
},
"domain": {
"update": {
"stroke": {"value": "#333"},
"strokeWidth": {"value": 1.5}
}
}
}
}
]

可以使用 的属性定义自定义文本 。例如,可以定义一个序数刻度,用作从轴值到轴标签文本的查找表。"text"``labels

图例(Legends)

图例可视化视觉值(如颜色、形状和大小)的比例映射。与刻度和轴类似,图例可以在顶级可视化中定义,也可以在组标记的范围内定义。

以下示例说明如何为填充颜色编码设置自定义字体和图例上的边框。encoding 块还使图例标签响应输入事件,并在鼠标悬停时更改文本颜色。labels

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
"legends": [
{
"fill": "color",
"encode": {
"title": {
"update": {
"fontSize": {"value": 14}
}
},
"labels": {
"interactive": true,
"update": {
"fontSize": {"value": 12},
"fill": {"value": "black"}
},
"hover": {
"fill": {"value": "firebrick"}
}
},
"symbols": {
"update": {
"stroke": {"value": "transparent"}
}
},
"legend": {
"update": {
"stroke": {"value": "#ccc"},
"strokeWidth": {"value": 1.5}
}
}
}
}
]

可以使用 的属性定义自定义文本 。例如,可以定义一个序数刻度,该刻度用作从支持到图例标签文本的查找表。注意:要在 orient 为 时执行自定义定位,请使用顶级 legendXlegendY 属性,请勿在自定义编码块中使用 and 属性。text labels value none x y

标题(title)

title 指令向图表添加描述性标题。与刻度、轴和图例类似,标题可以在规范的顶层定义,也可以作为组标记的一部分定义。

以下示例说明如何为标题和副标题文本标记设置自定义颜色和字体属性,以及如何启用副标题文本的交互性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"title": {
"text": "Title Text",
"subtitle": "Subtitle Text",
"encode": {
"title": {
"enter": {
"fill": {"value": "purple"}
}
},
"subtitle": {
"interactive": true,
"update": {
"fontStyle": {"value": "italic"}
},
"hover": {
"fontStyle": {"value": "normal"}
}
}
}
}

图形标记(Marks)

图形标记使用几何基元 (如矩形、线条和绘图符号) 直观地对数据进行编码。标记是可视化效果的基本可视化构建块,提供基本形状,其属性可以根据支持数据进行设置。标记属性定义可以是简单的常量或数据字段,也可以使用比例尺将数据值映射到视觉值。

支持的标记类型

  • arc - 圆弧,包括饼图和圆环切片。
  • area - 具有水平或垂直对齐方式的填充区域。
  • image - 图像,包括图标或照片。
  • group - 其他标记的容器,对子图很有用。
  • line - 描边线条,通常用于显示随时间的变化。
  • path - 使用 SVG 路径语法定义的任意路径或多边形。
  • rect - 矩形,如条形图和时间线所示。
  • rule - 规则是线段,通常用于轴刻度和网格线。
  • shape - 路径标记的一种特殊变体,用于更快地绘制制图地图。
  • symbol - 绘制符号,包括圆形、正方形和其他形状。
  • text - 具有可配置字体、对齐方式和角度的文本标签。
  • trail - 可以根据基础数据更改大小的线条。

视觉编码(Visual Encoding)

每个标记都支持一组视觉编码属性,这些属性确定标记实例的位置和外观。通常,每个输入数据元素生成一个标记实例;例外情况是 AND 标记类型,它们将多个数据元素表示为单个线条或区域形状。line area

标记定义通常如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"type": "rect",
"from": {"data": "table"},
"encode": {
"enter": {
"y": {"scale": "yscale", "field": "value"},
"y2": {"scale": "yscale", "value": 0},
"fill": {"value": "steelblue"}
},
"update": {...},
"exit": {...},
"hover": {...}
}
}

有三个主要属性集:enterupdateexit。首次处理数据并将 mark 实例新添加到场景中时,将评估 enter 属性。将针对所有现有(非现有)标记实例评估更新属性。当删除支持标记的数据时,将评估 exit 属性,因此标记将离开视觉场景。要更好地了解 enter、update 和 exit sets 的工作原理,请查看 Mike Bostock 的 Thinking with Joins

此外,当鼠标光标悬停在标记实例上时,可选的悬停集将确定视觉属性。鼠标松开时,将应用更新集。

信号/变量(signals)

信号是参数化可视化的动态变量,可以驱动交互式行为。信号可以在整个 Vega 规范中使用,例如,用于定义标记属性或数据转换参数。

信号值是被动的:它们可以响应输入事件流、外部 API 调用或上游信号的更改而更新。事件流捕获和排序输入事件,例如 或 。当事件发生时,具有关联事件处理程序的信号将按其规范 Sequences 重新计算。然后,更新的信号值会传播到规范的其余部分,并且可视化会自动重新呈现。mousedown``touchmove

信号定义及其在规范其余部分的用法如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
"signals": [
{
"name": "indexDate",
"description": "A date value that updates in response to mousemove.",
"update": "datetime(2005, 0, 1)",
"on": [{"events": "mousemove", "update": "invert('xscale', x())"}]
}
],
"data": [
{ "name": "stocks", ... },
{
"name": "index",
"source": "stocks",
"transform": [
{
"type": "filter",
"expr": "month(datum.date) === month(indexDate)"
}
]
}
],
"scales": [
{ "name": "x", "type": "time", ... }
],
"marks": [
{
"type": "rule",
"encode": {
"update": {
"x": {"scale": "x", "signal": "indexDate"}
}
}
}
]
}

事件流(Event Streams)

事件流是建模用户输入以实现动态、交互式可视化的主要方式。事件流捕获一系列输入事件,例如鼠标单击、触摸移动、计时器时钟周期或信号更新。当与流定义匹配的事件发生时,它们会导致评估任何相应的信号事件处理程序,从而可能更新信号值。

1
2
3
4
5
6
7
8
9
{
"name": "signalName",
"on": [
{
"events": <<event-stream-definition>>,
"update": ...
}
]
}

表达式(expressions)

由于vega在设计上要求完全用json来描述数据。所以在一些复杂的逻辑处理上引入了表达式这么一个概念,其具体表现为一个符合json规范的的,支持所有基本的算术、逻辑和属性访问表达式的JavaScript 的受限子集。

表达式语言参考

布局(layout)

布局将组标记的集合放置在网格中,从而简化了小型序列图和协调多个视图显示的组成。当应用于规范的顶层或组标记时,将根据布局规范收集和定位所有直接的子组标记。布局引擎支持流布局以及列、行和网格对齐布局。

布局引擎还支持包含行和列的页眉和页脚单元格,以及行标题和列标题单元格。要指示页眉、页脚和标题,这些组的规范必须包含设置为以下 、 或 之一的属性。页眉、页脚或标题元素的数量应与表中的行数或列数匹配。如果元素较少,则某些单元格将留空。如果元素过多,将忽略其他元素并记录警告。role``column-header``column-footer``column-title``row-header``row-footer``row-title

布局中组的顺序取决于规范顺序 (跨组标记定义) 和内部标记排序 (对于单个组标记定义中的多个组实例)。组标记定义在规范中的显示顺序决定了它们在布局中的顺序。在具有多个组实例的单个组标记规范中,组项的内部排序决定了它们的渲染顺序和它们在布局中的顺序。可以使用 sort 指令修改内部顺序

JSON 解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
// Vega 架构的 URL。
"$schema": "https://vega.github.io/schema/vega/v5.json",
// 可视化的文本描述。在版本中≥ 5.10,则说明确定 Vega 视图的 container 元素的 aria-label 属性。
"description": "A specification outline example.",
// 整个视图的背景色 (默认为 transparent)。如果 signal-value≥ 5.10,则提供的表达式将用作基础信号定义的属性。
"background": "transparent"
"width": 500,
"height": 200,
// 要在可视化效果周围添加的像素填充。如果是数字,则指定所有边的填充。如果是对象,则值的格式应为 .在自动调整大小布局完成后应用填充。如果 signal-value{"left": 5, "top": 5, "right": 5, "bottom": 5}
"padding": 5,
// 设置应如何确定可视化大小。如果是字符串,则应为 (default) .对象值还可以指定用于内容大小调整和自动调整大小的参数。pad fit fit-x fit-y none
"autosize": "pad",
// 具有标记、轴和图例默认值的配置设置。
"config": {}
// 信号是参数化可视化的动态变量。
"signals": [],
// 数据集定义和转换定义要加载的数据以及如何处理这些数据。
"data": [],
// 数据映射
"scales": [],
// 地图投影 将 (经度、纬度) 对映射到投影的 (x, y) 坐标。
"projections": [],
// 坐标轴可视化空间比例尺映射。
"axes": [],
// 用于描述可视化的标题文本。
"legends": [],
// 图形标记使用几何基元 (如矩形、线条和绘图符号) 直观地对数据进行编码。
"marks": [],
// 表示图表数据矩形的顶级组标记的视觉属性的编码指令。例如,这可用于设置绘图区域(而不是整个视图)的背景填充颜色。
"encode":{},
// Vega 解析器将忽略的可选元数据。
"usermeta":{},
}

总结

Vega schema胜在描述图元和数据映射上比较通俗易懂更加贴近自然语言,而在逻辑的描述上比如数据变换(transforms)、数据处理/表达式(expressions)则过于抽象。由于json格式的限制,vega内置了大量的数据处理模版、以及一个JavaScript 的受限子集这部分过于依赖文档,想要灵活运用会有一个很艰苦的学习过程。

并且由于Vega其特殊的表达式形式,为了方便的输出其内部的内部的调试信息和异常提示,首先需要将调试消息添加到表单式中,同时Vega视图必须设置正确的日志记录级别。

参考 & 引用

https://vega.github.io/schema/vega/v5.json

https://vega.github.io/vega/

https://vega.github.io/vega/docs/expressions/

https://stackblitz.com/edit/vitejs-vite-rgqtvt5z?file=src%2Fcomponents%2FHelloVega.vue