近几年随着大数据逐渐火热,数据可视化也就显得格外重要,Ben Fry在他的著作《Visualiziing Data》中将数据可视化的过程分为七个步骤:
- 获取
- 分析
- 过滤
- 挖掘
- 表现
- 改善
- 交互
前面4步分别属于数据采集、数据分析、数据处理和数据挖掘领域,数据可视化的处理范围主要是后面三步。
(一)技术选型
本文是基于实验室做的智能客运公交wifi系统对后台数据进行可视化处理,分析业务需求主要是将新增关注人数、取消关注人数和每车最大连接数以折线图的形式动态展示。
整个数据可视化方案实施近两个星期,前期进行可视化技术选型,由于数据可视化插件种类和质量参差不齐,其中包括echarts(百度开源)http://echarts.baidu.com/,底层基于canvas;highcharts https://www.hcharts.cn/,基于svg,老牌图表库,商业用途需购买版权;还有一个就是D3.js https://d3js.org/可自由设计图表,适合展示丰富多样的图表样式。对于这三种插件进行以下分析:
1、兼容性
使用每个插件之前必须考虑它的兼容性问题,否则项目完成后发现部分浏览器不能用就需要重新选型或解决兼容性问题,事倍功半,得不偿失!
- Highcharts 兼容 IE6 及以上的所有主流浏览器,完美支持移动端缩放、手势操作。
- Echarts 兼容 IE6 及以上的所有主流浏览器,同样支持移动端的缩放和手势操作。
- D3 兼容IE9 及以上的所有主流浏览器,对于移动端的兼容性也同上。
目前这三种插件基本满足兼容性要求。
2、是否开源
- Highcharts 非商业免费,商业需授权,代码开源。
- Echarts 完全免费,代码开源。
- D3 完全免费,代码开源。
由于本项目是后台管理系统做数据可视化,不涉及商业用途,所有就是否开源方面选择哪个框架意义不大。
3.难易程度
- Highcharts 基于SVG,方便自己定制,但图表类型有限。
- Echarts 基于Canvas,适用于数据量比较大的情况。
- D3.v3 基于SVG,方便自己定制;D3.v4支持Canvas+SVG,如果计算比较密集,也可以选择用Canvas。除此之外,D3图表类型非常丰富,几乎可以满足所有开发需求,但代码相对于以上两个插件来说,会稍微难一点。
总的来说,我认为Echarts是国内做的最好的图表库,有多样的图表类型,色彩丰富,交互友好,易上手;Highcharts作为老牌图表库,国外开源项目,技术相对成熟,代码有社区人员维护,如果项目中对图表样式和展现效果没有严格要求,可以考虑用以上两种。我第一次接触D3,是在官网上看到的,酷炫的案例让我眼前一亮,马上在网上买了一本《精通D3.js》,学习之后发现做数据可视化是件很有意思的事情。由于秉承着科研精神和工匠精神,加之之前一段时间接触过D3.v3,做过一些demo,多样的图表设计和丰富的展示效果深深吸引着我,所以项目做对后台数据可视化处理我选择D3.js作为开发工具。
(二)方案设定
完成技术选型之后,接下来就是方案设定,基于智能客运公交wifi系统后台数据可视化输出要求,我画了简单的交互设计稿,如下图所示:
主要有四类数据:新增关注人数、取消关注人数、付费人数和最大连接数,其中最大连接数还需选择车次,这四类可以设计4个控件,选择不同控件会呈现出相应的折线图,车次选择下拉菜单只有在点击最大连接数时才显示。
(三)方案实施
首先约定数据格式为以对象元素组成的数组类型。如:
dataset=[{x:'2017-11-01',y:20},{x:'2017-11-02',y:18},
{x:'2017-11-03',y:22},{x:'2017-11-04',y:26},
{x:'2017-11-05',y:16},{x:'2017-11-06',y:30},
{x:'2017-11-07',y:22},{x:'2017-11-08',y:28},
{x:'2017-11-09',y:30},{x:'2017-11-10',y:36}]
1、需要构建SVG。SVG可以理解为PS中的画布或者是一张白纸,可以填充任意颜色甚至可以为透明色,代码如下:
svg=d3.select(".container")
.append("svg")
.attr("class","drawSVG");
/* SVG样式 */
.drawSVG{
display: inline-block;
margin-right:10px;
width: 100%;
height:500px;
}
2、定义比例尺。需求中以近十天的日期作为横坐标,因此在做比例尺定义的时候需考虑非线性比例尺,即序列比例尺,定义域和值域一一对应。在定义比例尺之前需计算纵坐标的最大值,代码如下:
var gdpmax=0;
//遍历dataset.y,将字符串转换成数字
//d3.max无法计算字符串大小,因此需要将dataset.y中所对应的字符串转换成数字
var newDataset=[];
for(var i=0;i<dataset.length;i++){
numberY=Number(dataset[i].y);
newDataset.push(numberY);
}
var currGdp=d3.max(newDataset);
if (currGdp>gdpmax)
gdpmax=currGdp;
用于每次数据获取的过程都需通过AJAX调用后台数据,返回的结果在数字变字符串,在使用d3.max求最大值的时候,字符串如何计算,导致计算错误,因此需要将字符串转换为数字,储存在newDataset数组中,再利用d3.max(newDateset)计算纵坐标最大值。
定义比例尺代码如下:
xScale=d3.scaleOrdinal()//实现需要的非线性比例尺
.domain(date)//data=['2017-11-01','2017-11-02',...]
.range(space);//space=[0,90,180,...]
yScale=d3.scaleLinear()//横坐标比例尺
.domain([0,gdpmax*1.1])
.range([height-padding.top-padding.bottom,0]);
由于利用d3.scaleOrdinal()序列比例尺,定义域与值域一一对应,为了让定义域的个数决定值域个数,我设计了一个小技巧。
var sum=0;
for (var i=0;i<dat