这里是Leland Wilkinson写的The Grammar of Graphics (1999)的读书笔记。
The Grammar of Graphics这本书主要介绍生成“可感知图(perceivable graphs)”,即图形(graphics)的语法规则。图形语法包含了数学风格的语法与美学风格的语法,前者用来表示对数据与问题的抽象,后者将这种抽象映射到图形的视觉元素上。
图形和图表的区别在于图形是比图表更基本的视觉元素。比如,饼图本质上是一个在极坐标系中存在的柱状图。一旦认识到这一点,我们就可以创造出在极坐标下更多更复杂的图表。
深入对图形的理解可以避免探讨图表分类,在设计一个图表库时,你很难设计出足够多的图表类型来满足用户的各类需求。一个基于图表分类的图表库还面临随着图表量增大,而整个系统的结构变得复杂而难以维护。图表与图表之间的一些相似的部分也难以得到高效的复用。
从用户的角度理解这个问题,理解用户想要用数据分析什么,做什么是十分困难的,很多库和工具直接放弃理解这一点而直接提供给用户一些固定的图表类型选择;这本质上是直接给到用户探索分析的结果而不是让用户感受分析的过程。
面向对象设计(OOD)使用了一些方法和技巧来使系统变得更加灵活且更容易被复用。
- 对象是系统的基本组成部分
- 对象之间可以相互交流数据
- 对象的职能相对单一
- 1+1>2;对象并不职能,但当对象组合成一个完整的系统便具备了一定的智能。
- 对象是相对封闭的,它不对外暴露其内部的工作过程。
- 面向对象的系统往往是模块化的,一部分模块在不能正常工作后,其他模块仍能履行其职能。
- 对象可以能处理各种情况的输入,一个好的对象可以对各种输入(甚至是非法输入)作出相应的回应。
- OOD会促使设计者做更局通用性、一般性的设计
虽然OOD经常被其所带来的各种问题诟病,但对于图形这种场景,我们仍选用OOD来深入研究。OOD是一种研究图形的自然语言,这是由于图形本身就是一种对象。即便最终的探索不能应用在计算机领域,OOD仍能帮助我们组织想法和理解。
object-oriented analysis and design (Mey- er, 1988; Rumbaugh et al., 1991; Booch, 1994)
“图”是点的集合,数学上的图是不可见的,是一种抽象的概念。“图形”则是一种“图”的实体表现。从图到图形的过程其实是借助了美学或视觉上的一些属性,如大小和颜色。
一个面向对象的图形系统要求对这些属性做明确的定义,明确规定这些视觉元素到数据的映射规则以便我们在计算机中实现这样的一个系统。我们将创建图形的步骤分为3步:
- Specification
- Assembly
- Display
Specification(定义或描述)是一个将用户行为翻译成规范的语言的过程。用户不一定要熟悉这种语言,而是由系统自动化的将用户的操作行为转化为规范的语言。类似于在tableau中进行的维度度量的拖拽操作,最终被转化为vizQL的过程。可以参考Wilkinson在1996年提出的实现。
描述大致可以划分为6个模块:
- 数据:数据可视化基于的数据源。
- 转化:对数据的一些转化操作,如聚合、排序、筛选等。
- 标度:缩放操作,如log。
- 坐标:坐标系统,如极坐标系、笛卡尔坐标系。
- 元素:图形的视觉元素与属性,如颜色、大小、形状。
- 指引/提示:提示信息,如刻度、图例。
(这里可以看出antv/g2的设计基本完全参考了这一点)
在有了各部分的定义后,我们需要将这些定义组装(Assembly)成一个完整的图表。即在坐标系中放置这些图形并进行排布。作者说不在这本书的讨论范围内...
最终要将整个图表渲染展示(Display)出来。用于辅助生产、工业领域的图表对渲染绘制功能要求非常低;与之相反,动态图形或在科学领域中用于探索分析的图形,则需要更多的功能,如框选、下钻、上卷、联动等基于数据的操作( Becker and Cleve-
land (1987), Cleveland and McGill (1988), Cook and Weisberg (1994), and
Swayne _et al. _(1998) )。
现代的研究甚至将可视化的视觉元素扩展到了触觉与听觉。
对于该图标,我们按照specification, Assembly, Display三个步骤进行分析。
{
ELEMENT: point(position(birth*death), size(0), label(country))
ELEMENT: contour(position(
smooth.density.kernel.epanechnikov.joint(birth*death)),
color.hue())
GUIDE: form.line(position((0,0),(30,30)), label("Zero Population Growth"))
GUIDE: axis(dim(1), label("Birth Rate"))
GUIDE: axis(dim(2), label("Death Rate"))
}
在生成图表时,我们需要根据一定的顺序来构建整个图表。图1.2展示的是一棵树,这棵树上的每一个节点都对应着图1.1中的某一个图形对象。图表中的每一条边,代表着一种关系。菱形箭头代表“拥有关系”,而三角箭头代表“是”的关系。
- “拥有”关系代表一种组合、聚合;
- “是”关系代表一种继承
通过图1.2,我们能感知到绘制图形的整个过程。在对图表进行修改时,我们不再需要对底层定义进行复杂的变动,而是直接“添加一个图形”,稍微变动树上的节点即可。
借助图形语法,我们尝试对图1.1做一些改造。
如
ELEMENT: polygon(
position(smooth.density.kernel.epanechnikov.joint(birth*death)),
color.hue()
)
图1.3:这里我们使用polygon(多边形)来替换之前的contour(曲线);多边形的颜色(hue)代表这个在该点估算出的国家的密度(根据数量计算出,但数量是在某一个点,我们需要把这些点以密度的形式分散到周围的各个点)
ELEMENT: contour(
position(smooth.density.kernel.epanechnikov.joint(birth*death),
color.hue()
))
ELEMENT: point(position(birth*death), size(military))
DATA: longitude, latitude = map(source("World"))
TRANS: bd = max(birth-death, 0)
COORD: project.mercator()
ELEMENT: point(
position(lon*lat),
size(bd),
color(color.red)
)
ELEMENT: polygon(position(longitude*latitude))
探索图形语法的目的不是发明一门专门的图形语言,而是在现有流行编程语言的基础上引入图形语法的思想与概念。这能在很大程度上解决目前大多数图表绘制库无法绘制一些非常见的图表类型的问题。
作者还提及到,图形语法并不是一种分类,虽然书中提及对图形的一些分类,但这些分类只是为了探索性的服务于OOD,并不一定是最优的分类方法。
在数据挖掘领域,我们必须更加系统的构建图形来应对多变复杂的情况,许多数据挖掘系统仍在使用饼图,柱状图及数据切片(分面系统)进行可视化,但这些简单的图表无法表示数据与数据之间的关系;这本质上都是因为这些系统缺乏更深层次的图形语法造成的。他们只是对数据切片做简单的分面展示。另一类复杂的场景则是如分类树、神经网络等。