一、总体思路
数据从扁平的表格,变成一个多维度多层次的图表的过程,其实是一个提取数据内在逻辑和关联性,以及利用视觉元素(位置,大小,颜色…)的转换过程。在过程中通过对数据进行归类、数值调整、映射,把一组数据转换成一个图表,一个简单的流程大致如下:
分组 -> 数值化 -> 归一化 -> 调整(Adjust Graphic Properties) -> 绘制
二、 数据处理流程
以年份-销售数额为例,画一个分组柱状图,简单模拟下这个过程
const data = [
{ year: '1951 年', sales: 38, type: '品类1' },
{ year: '1952 年', sales: 52, type: '品类1' },
{ year: '1951 年', sales: 48, type: '品类2' },
{ year: '1952 年', sales: 57, type: '品类2' },
];
1、 分组
根据 type 字段进行分组
[
{ year: '1951 年', sales: 38, type: '品类1' },
{ year: '1952 年', sales: 52, type: '品类1' },
];
[
{ year: '1951 年', sales: 48, type: '品类2' },
{ year: '1952 年', sales: 57, type: '品类2' },
];
分组会产生一个新的维度,同一组的数据在一个特征上会保持一致,在分组柱状图上,表现出来的特征就是颜色和同一个 x 轴数值上的位置。
2、 数值化
原始数据中,不是每一个字段都是数值,通常情况下,x 轴(也叫维度轴)一般是一些枚举字符串,例如时间,年份等。但在图表的坐标轴(以笛卡尔坐标系为例)中,x 轴和 y 轴都是数值,所以需要对原始数据进行数值化,使用数字代替原始数据。
数值化后:
[
{ year: 0, sales: 38, type: 1 },
{ year: 1, sales: 52, type: 1 },
];
[
{ year: 0, sales: 48, type: 2 },
{ year: 1, sales: 57, type: 2 },
];
在数值化之前,会对原始数据进行"备份保存",例如放在一个 origin_字段内。
至此,其实绘制一个简单图表的数据已经具备了。x 轴是年份,y 轴是销售额,对应坐标轴上的刻度——1、2、3……上,用不同颜色的笔(type-1 用红色笔,type-2 用蓝色笔)画点,就可以得到一张图了。
3、归一化
从这一步起,数据处理的目的就更服务于图形绘制。由于不同的数据起点和终点不同,在绘制坐标轴刻度值时,需要先将数据轴字段归一化。绘制数据点时,将坐标轴长度视为 1,将实际数据,最小值对对应 0,最大值对应 1,映射到坐标轴相应位置。
实际的渲染中,x 轴左右会留出空白,所以在这里,其实原始数据映射的范围不是 0 ~ 1,这里取 0.25 ~ 0.75 。归一化后的数据:
[
{ year: 0.25, sales: 0, type: 1 },
{ year: 0.75, sales: 0.7368421052631579, type: 1 },
];
[
{ year: 0.25, sales: 0.5263157894736842, type: 2 },
{ year: 0.75, sales: 1, type: 2 },
];
4、 调整(adjust)**映射至图形属性**
调整是图形绘制前特别关键的一个步骤。以柱状图类为例,柱状图有不同的组合形态,比如分组柱状图,堆积柱状图,瀑布图。根据需要绘制的图形不同,会引入不同的图形参数。一般通过 adjust 字段,指定不同的调整策略,实现不同的组合方式。
画一个矩形(柱体),除了已知的数据点(point: {x, y })以外,还需要几个参数才能画出来?
答案是三个: p(数据点)、size、y0。
为了确定一个柱子绘制起点,高度,宽度。会额外引入 y0 和 size,两个参数。size 会根据需要渲染的数据动态调整。y0 在分组柱状图中是 0,但是在堆积柱状图中,就是同簇中,上一个数据点的 y 值。
除了根据 adjust 类型,对引入的图形参数进行调整,也会对数据点进行调整。比如上面的分组柱状图,对 x 轴的字段数值进行了调整,分别向两边偏移,柱子错开显示。堆积柱状图对 y 轴的字段数值进行调整。
在不同的图形(点、线,柱)和坐标系类型(多种极坐标系)中,引入的绘图参数不一样,调整的算法也不同。但是这个步骤是必须的,直接影响图表类型的。
5、绘制
绘制引擎可以有多种,目前主流图表库一般都支持 svg 和 canvas。主要用 canvas,具体绘制过程不在此赘述。