文章目录
- 前言
- 目标读者
- 正文
前言
ECharts 具有广大用户群,它是基于 JavaScript 开发的数据可视 化图表库。ECharts 支持几乎所有常用图表,对地理信息图形化和数 据化 3D 图形也支持良好。ECharts 本身具备丰富的示例,并且具有比 较完善的文档支撑,因此 ECharts 上手使用也相当简便快捷,完全满 足快速开发所见即所得的要求。
尤其值得强调的是,ECharts 提供了非常灵活的个性化定制属性。对于不同的应用场景和设计需求,ECharts 处理起来都游刃有余,可谓是数据分析师的良师益友。
本文将手把手把手教你“手撕”一个以 ECharts 为工具的整合多层级数据,创建一个相对复杂的数据大屏,并且根据需求进行个性定制的例子。
目标读者
如果你想学习数据处理和分析,那么本文是为你准备的。本文将使用既容易理解,数据处理又相对简单的案例,带领你了解使用pandas进行数据处理的过程,以及如何选择数据展现模型进行数据可视化。
如果你想了解和学习Web前台开发的相关技能,并且想熟练使用Echart,本文将带你学习Echart使用和配置方法,并进一步掌握更加复杂的数据可视化方法。对于如何配置Echarts中对象属性,本文也会耐心介绍如何查找和配置ECharts对象属性。
如果你想学习python,并且想掌握Web后台技能,并搭建一个Web开发框架。那么本文也非常适合你。本文将搭建一个轻便极易上手的,以python为开发语言的Web框架。
简而言之,本文适合想入门大数据处理和数据可视化,想使用python进行快速开发Web应用的读者。
正文
本案例假设有这样一个需求:需要统计和显示各个城市以及下辖工厂的人员数量,对各个城市从业人员数量,以及工厂的人员数量进行排序。
笔者已设计好数据库表模型,并且生成了案例所需数据。为了简化开发,并且方便初学者学习使用,本文使用sqlite数据库。下面的数据表city是存储在sqlite中的一个表,共计635条数据,其表结构如下:
其中包括的数据样例如下:
每个城市,以及其下辖工厂数量和城市工厂从业总人数统计如下:
本案例使用python开发,数据操作使用pandas库。pandas可以帮助我们快速的进行数据清洗和处理,只需几条语句就可以将数据整理成我们想要的格式。
因为需要统计每个城市工厂从业总人数并排序,还要排序城市下辖所有工厂人员数量。有两种思路解决这个问题。一是使用多个图表展示数据:一个图表按照城市分类将这一级别的工厂从业人员数据统计好,使用柱形图进行显示排序,接着再使用另一个图表展示城市下辖所有工厂的人员数量,按照多少排序显示;另外有一种方法是将所有数据都展示在同一个图表里。
从数据处理过程的复杂度看,虽然第一种方法更符合数据分层处理和分析的过程,但是很明显的缺点是处理过程更加繁琐。相对来说,第二种方式在数据展示时更加方便快捷,但是对数据的处理和组织能力要求更高,需要深入了解pandas对数据对象的各种操作。
本案例先使用第二种方法实现需求,随后会根据优化要求使用第一种方法再完善图表显示。
Echarts提供了能够展示多层级数据的图表,比如旭日图和树图。从本质上看,这两种图表需要的数据结构是相同的,并且具有相似的对象元素,所以只要掌握其中一种展示方法即可。因为旭日图在展示时更易于比较数据的百比大小。本案例使用旭日图进行展示。
Echarts提供了各种类型旭日图的示例。我们从这些优秀的例子中,选择下面这个基础例子为原型,最终一步一步修改成本案例需要的展示图形。
Echarts旭日图基础案例。
从上面案例展示的源码分析,其中描述图形的json数据结构分为4个层级,每一层级是一辈人,图上显示的就是四世同堂的图像。而我们当前只需要两个层级,第一个层级是父级,对应到本例的数据是每个城市从业人员数量;第二个层级是子级,对应到本例就是每个城市下所有工厂的人员数量。
按照我们的分析结果和数据要求,构造的json数据结构如下:
[{
"value": 18403,
"name": "城市01",
"path": "城市01",
"children": [{
"path": "城市01",
"name": "工厂01",
"value": 59
}, {
"path": "城市01",
"name": "工厂02",
"value": 57
}]
}]
这个json数据是一个数组,数组中有多个对象,每个对象对应本例中的一个城市,每个对象具有name,value,path,children四个属性,其中children是一个数组,数组中的每一个对象是一个工厂的人员数量,每个对象有path,name,value三个属性。其中path属性在父对象和子对象都存在,这就是两者联系的纽带了。
我们按照分析写出自己的静态页面,主要部分代码如下:
<script type="text/javascript">
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
var srData= [{
"value": 18403,
"name": "城市01",
"path": "城市01",
"children": [{
"path": "城市01",
"name": "工厂01",
"value": 59
}, {
"path": "城市01",
"name": "工厂02",
"value": 57
}, {
"path": "城市01",
"name": "工厂03",
"value": 883
}, {
"path": "城市01",
"name": "工厂04",
"value": 982
}, {
"path": "城市01",
"name": "工厂05",
"value": 93
}, {
"path": "城市01",
"name": "工厂06",
"value": 572
}, {
"path": "城市01",
"name": "工厂07",
"value": 492
}, {
"path": "城市01",
"name": "工厂08",
"value": 752
}, {
"path": "城市01",
"name": "工厂09",
"value": 813
}, {
"path": "城市01",
"name": "工厂10",
"value": 5
}, {
"path": "城市01",
"name": "工厂11",
"value": 739
}, {
"path": "城市01",
"name": "工厂12",
"value": 233
}, {
"path": "城市01",
"name": "工厂13",
"value": 785
}, {
"path": "城市01",
"name": "工厂14",
"value": 400
}, {
"path": "城市01",
"name": "工厂15",
"value": 599
}, {
"path": "城市01",
"name": "工厂16",
"value": 120
}, {
"path": "城市01",
"name": "工厂17",
"value": 906
}, {
"path": "城市01",
"name": "工厂18",
"value": 376
}, {
"path": "城市01",
"name": "工厂19",
"value": 117
}, {
"path": "城市01",
"name": "工厂20",
"value": 506
}, {
"path": "城市01",
"name": "工厂21",
"value": 822
}, {
"path": "城市01",
"name": "工厂22",
"value": 326
}, {
"path": "城市01",
"name": "工厂23",
"value": 985
}, {
"path": "城市01",
"name": "工厂24",
"value": 620
}, {
"path": "城市01",
"name": "工厂25",
"value": 990
}, {
"path": "城市01",
"name": "工厂26",
"value": 63
}, {
"path": "城市01",
"name": "工厂27",
"value": 449
}, {
"path": "城市01",
"name": "工厂28",
"value": 139
}, {
"path": "城市01",
"name": "工厂29",
"value": 212
}, {
"path": "城市01",
"name": "工厂30",
"value": 234
}, {
"path": "城市01",
"name": "工厂31",
"value": 273
}, {
"path": "城市01",
"name": "工厂32",
"value": 646
}, {
"path": "城市01",
"name": "工厂33",
"value": 0
}, {
"path": "城市01",
"name": "工厂34",
"value": 885
}, {
"path": "城市01",
"name": "工厂35",
"value": 571
}, {
"path": "城市01",
"name": "工厂36",
"value": 150
}, {
"path": "城市01",
"name": "工厂37",
"value": 413
}, {
"path": "城市01",
"name": "工厂38",
"value": 694
}, {
"path": "城市01",
"name": "工厂39",
"value": 442
}]
}];
const formatUtil = echarts.format;
function getLevelOption() {
return [
{
itemStyle: {
borderWidth: 0,
gapWidth: 5
}
},
{
itemStyle: {
gapWidth: 1
}
},
{
colorSaturation: [0.35, 0.5],
itemStyle: {
gapWidth: 1,
borderColorSaturation: 0.6
}
}
];
}
myChart.setOption(
(option = {
title: {
text: 'People of the factory in the city',
left: 'center'
},
animation: false,
tooltip: {
formatter: function (info) {
var value = info.value;
var treePathInfo = info.treePathInfo;
var treePath = [];
for (var i = 1; i < treePathInfo.length; i++) {
treePath.push(treePathInfo[i].name);
}
return [
'<div class="tooltip-title">' +
formatUtil.encodeHTML(treePath.join('/')) +
'</div>',
'Head Count: ' + formatUtil.addCommas(value)
].join('');
}
},
series: [
{
name: 'People of the factory in the city',
type: 'sunburst',
radius: ['40%', '80%'],
nodeClick: 'link',
visibleMin: 300,
label: {
show: true,
formatter: '{b}'
},
itemStyle: {
borderColor: '#fff'
},
levels: getLevelOption(),
data: srData
}
]
})
);
option && myChart.setOption(option);
</script>
使用Echarts前需要引入Echarts库。使用下面语句时,需要指定cdn路径或者具体路径。
<script src="cdn_path/echarts.min.js"></script>
让图形显示在界面上,先创建画布。
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
Echarts中承载图形属性的对象是option,其中最重要的对象是series,这个对象中可以包含一个或者多个图形对象,我们的sunburst图形就在这个对象中。
对于具体的对象属性含义可以参考Echarts配置手册。
从上面静态页面的json结构可以看出,我们只列出了城市01的数据。在本地打开静态页面,其效果如图:
图像正确的显示了层级关系。值得特别注意的,在静态页面中,我们并没有按照人数排序对象数组,而旭日图已经按照数据的多少,从右上开始顺时针显示。接下来,按照json数据结构的分析,我们尝试组织好了全部的数据补充到静态页面。图像效果如图:
页面最终显示出来的结果还是挺绚丽壮观的。各个城市已经按照人员数量所占的百分比大小显示了,图像上每个大扇区表示对应城市从业人员数量,每个小扇区代表所属城市下每个工厂人员数量。
当前数据可视化的结果基本满足当前项目的需求。
令人不快的是,页面上显示的工厂名称黑压压一片连在一起,工厂名称无法看清,并且整体也不美观,这个问题是否能解决呢?
好在Echarts是很成熟的图形库,其文档也相当完善。我们尝试通过查找文档来解决这个问题。
首先找到文档位置配置手册的位置:https://echarts.apache.org/zh/option.html#title
因为我们使用的是旭日图,找到旭日图的配置位置,如图:
我们希望旭日图中每个小扇形足够宽的时候再显示工厂名称,这样工厂名称才能看得清楚一些。我们看看label下面有没有能够修改这一配置的属性。经过一番查找,我们发现旭日图的label对象中包括如下这个属性。
配置label对象的minAngle的含义是,该属性设置每个分类在旭日图的扇形中是否显示名称,当扇形的角度小于设置的角度就不再显示名称。在静态页面上修改后再试试看,果然有效果,页面一下变的清爽多了。对于感兴趣的城市,我们将鼠标指针指上去,就会弹出该扇区的名称和人员数量的信息。
前台静态页面搞定了,界面数据可视化部分已经没有问题了。
接下来我们把注意力转移到后台。
后台数据的处理和将数据传递给前台才是重头戏,由于文章篇幅的关系,我们将在下一篇文章中描述。