You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
16 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 37 | 实战(一):如何使用图表库绘制常用数据图表?
你好,我是月影。
图表是我们在可视化中展示数据常用的方式之一,常见的有柱状图、折线图、饼图等等,我们之前也都一一实现过。如果我让你总结一下图表的实现方法,你能说出来吗?总结不出来也没关系,这节课,我们就一起梳理一下实际项目中常用的制图方法。
实现图表有两种方式,一是使用现成的图表库,二是使用数据驱动框架,前者胜在简单易用,后者则更加灵活。所以,我们会用两节课的时间分别来讲,使用图表库和使用数据驱动框架的制图思路。
因为使用图表库更加简单,所以这一节课我们先来讲怎么使用它实现图表。另外,这节课的实践性比较强,我建议你跟着我的讲解一块儿动手去写,这样能更快地理解课程的内容。
## 课前准备
说了这么多我们今天到底会用哪些图表库呢我们主要会使用我们比较熟悉的SpriteJS和QCharts来绘制图表。其他的图表库例如更常用的[ECharts](https://echarts.apache.org/),它在图表实现上的原理和用法和我们今天讲的基本相同,所以学完了今天的内容,你完全可以根据它的教程文档去自学。
好了确定了要使用的工具之后我们就要配置和加载SpriteJS和QCharts。具体怎么做呢
最简单的方式是我们直接通过CDN用script标签来加载SpriteJS和QCharts打包好的文件。
```
<script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
<script src="https://unpkg.com/@qcharts/core@1.0.25/dist/index.js"></script>
```
如果是完整的工程项目的话你也可以使用webpack来打包和加载模块。这里有一个[Quick Start](https://github.com/qcharts/quickstart)你可以fork这个项目按照其中的配置项来设置。当加载完成之后我们就可以开始绘制基础的图表了。
## QCharts图表的基本用法
QCharts图表由图表Chart对象及其子元素构成。其中图表对象的子元素包含图形Visual和其他插件Plugin)。图形是必选元素,其他的插件都是可选元素。
图表在构建的时候需要传入一个DOM元素这个元素可以是页面上任意一个块级元素QCharts用这个元素作为容器来创建Canvas画布并其还会根据容器的大小来设置Canvas画布的大小。默认情况下图表会根据画布大小来自动适配。
接下来我们看一下具体的操作。首先我们创建一个图表对象设置它的容器设置成一个id为app的元素。
```
const { Chart, Line } = qcharts;
const chart = new Chart({
container: '#app'
});
```
创建了这个容器之后,我们就可以为它绑定数据,假设我们绑定一份销售数据。
```
const data = [
{ date: '05-01', category: '图例一', sales: 15.2 },
{ date: '05-02', category: '图例一', sales: 39.2 },
{ date: '05-03', category: '图例一', sales: 31.2 },
{ date: '05-04', category: '图例一', sales: 65.2 },
{ date: '05-05', category: '图例一', sales: 55.2 },
{ date: '05-06', category: '图例一', sales: 75.2 },
{ date: '05-07', category: '图例一', sales: 95.2 },
{ date: '05-08', category: '图例一', sales: 100 }
];
chart.source(data, {
row: 'category',
value: 'sales',
text: 'date'
});
```
如上面代码所示我们将数据内容与图表对象通过chart.source方法绑定起来。绑定数据的时候我们可以指定数据的行、数值和文本这些设置会被图形Visual和其他插件使用。这里我们将行设为category这个字段数值设为sales字段文本设为data字段。
设置之后图表并没有马上显示出来。这是因为我们还没有给图表指定图形。QCharts支持多种图形对象比如Line、Area、Bar等等。假设我们选择了Line对象那我们只需要创建它然后将它添加到chart对象的子元素中就可以了。
```
const line = new Line();
chart.append([line]);
```
这样,我们就让图形显示出来了,效果如下:
![](https://static001.geekbang.org/resource/image/b6/9f/b6a87f9984082b6a02a99b484262ef9f.jpeg?wh=1920*1080)
了解了QCharts图表的基本用法之后我们一起进入实践环节吧。
## QCharts绘制折线图、面积图、柱状图和饼图的方法
我们先来讲讲折线图、面积图、柱状图和饼图的实现方法,因为之前已经实现过很多次了,所以理解起来比较容易。
首先是折线图它是可视化中最常用的图表之一。刚才我们用Line图形绘制了一条折线但它还不是完整的折线图。完整的折线图除了有图形以外还要有表示数据的坐标轴、提示信息和图例这些都需要在QCharts中由插件来完成。
接下来,我们就继续给前面的代码添加元素。首先,我们给它增加两个坐标轴,分别是底部和左侧的。
我们通过Axis类来创建axis对象默认的坐标轴是在底部不用修改再通过.style改变它的样式属性比如我们将"grid"设置为false这样画布上不会显示纵向的网格线。
```
const axisBottom = new Axis().style("grid", false);
```
然后我们同样创建一个axis对象通过设置orient: “left” 让它朝左,这样就成功创建了左侧的坐标轴。同样,我们也要把它样式中的"axis"属性设置为false这样画布上就不会显示坐标轴的实线了。
```
const axisLeft = new Axis({ orient: "left" })
.style("axis", false);
```
最后我们将两个坐标轴添加到chart对象的子元素中去就可以将坐标轴显示出来。
![](https://static001.geekbang.org/resource/image/62/84/62c0556935f2756c6eac5a88057eb784.jpeg?wh=1920*1080)
添加完坐标轴之后我们再添加图例Legend和提示Tooltip。最简单的方式是我们直接创建两个对象然后将它们添加到charts对象的子元素中。
```
const legend = new Legend();
const tooltip = new Tooltip();
chart.append([line, axisBottom, axisLeft, legend, tooltip]);
```
添加了图例和提示信息后的图表如下图所示:
![](https://static001.geekbang.org/resource/image/58/85/5896a90b8979ecaf081726195f632985.jpeg?wh=1920*1080)
**接下来我们再来说说怎么用QCharts绘制面积图和柱状图。**
学会了折线图的绘制方法我们就可以用相同的思路非常简单地绘制其他图形了比如说我们可以简单将Line对象换成Area或Bar对象这样就可以用同样的数据绘制出面积图或柱状图。这是为什么呢
![](https://static001.geekbang.org/resource/image/2b/bf/2b29397b83cdbe7630bd88982d717cbf.jpeg?wh=1920*1080 "将 Line 改成 Area 绘制出面积图")
![](https://static001.geekbang.org/resource/image/c7/fd/c70fe007a4e74124a8e23afbb59175fd.jpeg?wh=1920*1080 "将 Line 改成 Area 绘制出柱状图")
因为像折线图、面积图、柱状图这些表现形式不同的图表,它们能够接受同样格式的数据,只是想要侧重表达的信息不同而已。一般来说,折线图强调数据变化趋势,柱状图强调数据的量和差值,而面积图同时强调数据量和变化趋势。在实际项目中,我们要根据不同的需求选择不同的基本图形。
如果要强调整体和局部比例,我们还可以选择绘制饼图。与折线图、面积图、柱状图相比,饼图不用配置图例,因为图例会自动生成,而且饼图也不需要坐标轴,所以使用起来更加简单。
```
const data = [
{ date: '05-01', sales: 15.2 },
{ date: '05-02', sales: 39.2 },
{ date: '05-03', sales: 31.2 },
{ date: '05-04', sales: 65.2 },
{ date: '05-05', sales: 55.2 },
{ date: '05-06', sales: 75.2 },
{ date: '05-07', sales: 95.2 },
{ date: '05-08', sales: 100 }
];
chart.source(data, {
row: 'date',
value: 'sales',
text: 'date'
});
const pie = new Pie();
const legend = new Legend();
const tooltip = new Tooltip();
chart.append([pie, legend, tooltip]);
```
上面的代码用同样的数据绘制了一个饼图。
![](https://static001.geekbang.org/resource/image/12/7d/12dffaec3578995169de1db76491947d.jpeg?wh=1920*1080)
## QCharts绘制雷达图、仪表盘和玉玦图、南丁格尔玫瑰图
讲完了这些常见的基础图表,我们再来看几个比较有趣的图表。
首先是雷达图,它一般用来绘制多组固定数量的数据,可以比较直观地显示出这组数据的特点。我们经常会在游戏中看见它的应用,比如,下面这张图表就显示了三国武将诸葛亮的各方面数据。
![](https://static001.geekbang.org/resource/image/96/ae/96b37e3a3267470ce6a95e3c358dcbae.jpeg?wh=1920*1080)
雷达图的实现代码如下:
```
const data = [
{ date: '体力', category: '诸葛亮', sales: 100 },
{ date: '武力', category: '诸葛亮', sales: 69 },
{ date: '智力', category: '诸葛亮', sales: 100 },
{ date: '统帅', category: '诸葛亮', sales: 95 },
{ date: '魅力', category: '诸葛亮', sales: 95 },
{ date: '忠诚', category: '诸葛亮', sales: 100 },
];
chart.source(data, {
row: 'category',
value: 'sales',
text: 'date'
});
const radar = new Radar();
radar.style('section', (d) => ({ opacity: 0.3 }));
const legend = new Legend({ align: ['center', 'bottom'] });
const tooltip = new Tooltip();
chart.append([radar, legend, tooltip]);
```
除此之外,还有一些其他类型的图表,可以用来展示特殊的信息。比较典型的有仪表盘,它可以显示某个变量的进度。
![](https://static001.geekbang.org/resource/image/6a/9e/6a21ab85d811fa8a6abyy30ab0516b9e.jpeg?wh=1920*1080)
仪表盘实现代码比较简单,如下所示:
```
const gauge = new Gauge({
min: 0,
max: 100,
percent:60,
lineWidth: 20,
tickStep: 10
});
gauge.style('title', { fontSize: 36 });
chart.append(gauge);
```
如果我们要显示多个变量的进度,还可以使用玉玦图。
![](https://static001.geekbang.org/resource/image/b7/82/b76efc4a8c052fb8d11ba976ef039782.jpeg?wh=1920*1080)
对应的代码也非常简单,如下所示:
```
const data = [
{
type: '政法干部',
count: 6654
},{
type: '平安志愿者',
count: 5341
},{
type: '人民调解员',
count: 3546
},{
type: '心理咨询师',
count: 4321
},{
type: '法律工作者',
count: 3923
},{
type: '网格员',
count: 5345
}
].sort((a, b) => a.count - b.count);
chart.source(data, {
row: 'type',
text: 'type',
value: 'count'
});
const radialBar = new RadialBar({
min: 0,
max: 10000,
radius: 0.6,
innerRadius: 0.1,
lineWidth: 10
});
radialBar.style('arc', { lineCap: 'round' });
const legend = new Legend({
orient: 'vertical',
align: ['right', 'center'],
});
chart.append([radialBar, legend, new Tooltip()]);
```
最后是南丁格尔玫瑰图,它可以显示多维度的信息,比如下图就显示了男女游客在公园四个区域内的分布情况。南丁格尔玫瑰图的绘制思路和代码,我们在[第35节课](https://time.geekbang.org/column/article/288323)已经说过,这里就不再多说了。如果你还不熟悉,可以再去回顾一下。
![](https://static001.geekbang.org/resource/image/47/a8/47eca31ddbf1cb2423aa3db23bcabca8.jpeg?wh=1920*1080)
## QCharts绘制图表组合
我们刚才讲的都是在图表上绘制单一变量,那要想在图表上聚合多元变量,比如在一张天气图表上同时展示降水量、气温,我们可以同时绘制多个图形来表示。
我们可以分两种情况来讨论最简单的情况是用相同的图形来绘制不同的变量。这个时候我们可以直接通过不同category来绘制多个图形。比如下面的数据就是用两组category分别绘制了两条折线。
```
const data = [
{ date: "1月", catgory: "降水量", val: 15.2 },
{ date: "2月", catgory: "降水量", val: 19.2 },
{ date: "3月", catgory: "降水量", val: 11.2 },
{ date: "4月", catgory: "降水量", val: 45.2 },
{ date: "5月", catgory: "降水量", val: 55.2 },
{ date: "6月", catgory: "降水量", val: 75.2 },
{ date: "7月", catgory: "降水量", val: 95.2 },
{ date: "8月", catgory: "降水量", val: 135.2 },
{ date: "9月", catgory: "降水量", val: 162.2 },
{ date: "10月", catgory: "降水量", val: 32.2 },
{ date: "11月", catgory: "降水量", val: 32.2 },
{ date: "12月", catgory: "降水量", val: 15.2 },
{ date: "1月", catgory: "气温", val: 2.2 },
{ date: "2月", catgory: "气温", val: 3.2 },
{ date: "3月", catgory: "气温", val: 5.2 },
{ date: "4月", catgory: "气温", val: 6.2 },
{ date: "5月", catgory: "气温", val: 8.2 },
{ date: "6月", catgory: "气温", val: 15.2 },
{ date: "7月", catgory: "气温", val: 25.2 },
{ date: "8月", catgory: "气温", val: 23.2 },
{ date: "9月", catgory: "气温", val: 24.2 },
{ date: "10月", catgory: "气温", val: 16.2 },
{ date: "11月", catgory: "气温", val: 12.2 },
{ date: "12月", catgory: "气温", val: 6.6 },
];
chart.source(data, {
row: "catgory",
value: "val",
text: "date",
});
const line = new Line({ axisGap: true });
...
```
![](https://static001.geekbang.org/resource/image/b8/92/b88746440cae3937beb3c638b9bda392.jpeg?wh=1920*1080)
如果我们想用不同类型的图形来展示多个变量在QCharts中我们只需要创建多个不同的图形对象然后把它们都添加到chart对象的子元素中去就可以了。
```
const ds = chart.dataset;
const d1 = ds.selectRows("降水量");
const line = new Line({ axisGap: true })
.source(d1)
.style("point", { strokeColor: "#fff" });
const d2 = ds.selectRows("气温");
const bar = new Bar().source(d2);
bar.style("pillar", { fillColor: "#6CD3FF" });
```
如上面代码所示我们先可以通过chart.dataset拿到通过.source绑定给chart对象的数据集然后通过selectRows分别将降水量和气温数据过滤出来。接着我们分别创建Line和Bar两个图形对象再将降水量和气温数据分别绑定给它们。最后我们将这两个对象同时添加到chart子元素列表里就可以将两个不同类型的图形显示出来了具体的效果如下
![](https://static001.geekbang.org/resource/image/f8/62/f8688798100f4a9yyc04c9d34fc79262.jpeg?wh=1920*1080)
## 要点总结
这节课我们主要学习了QCharts图表库的使用。
QCharts是基于SpriteJS的简单可视化图表库我们通过它可以绘制各种类型的图表。一般来说我们是先创建图表对象然后绑定数据接着添加图形对象以及其他的插件包括图例和提示。通过将图形和插件以子元素的形式添加到图表对象上就能把图表内容最终显示出来了。
我们可以很方便地根据数据特点和业务需要,用数据绘制折线图、面积图、柱状图、饼图、雷达图等图表,还可以绘制特殊的仪表盘和玉玦图。另外,如果要显示多维数据,我们也可以用稍微复杂一些的南丁格尔玫瑰图。
最后,我们还可以把多维度变量聚合在一个图表中来显示不同的图形组合。具体操作是,我们先筛选数据,然后创建不同类型的图形对象,最后将它们都添加到图表对象的子元素中。
总的来说我们今天讲的其实都是QCharts图表库最基础、最常用的方法。QCharts还提供了众多其他类型的图表以及灵活操作图表样式的API。如果你有兴趣继续钻研可以通过我课后给出的参考链接进一步学习。
## 小试牛刀
你学会了使用不同图表来表达不同数据了吗你可以试着使用GitHub仓库里的北京市天气数据和空气质量数据实现一个温度、湿度、风速、空气质量的聚合图表吗如果用来展示温度、湿度、风速和空气质量的图形都不相同就更好了。
这些常用的制图方法你都学会了吗?下节课,我们会接着来学,怎么使用数据驱动框架来表达不同数据,挑战才刚刚开始呢,我们下节课再见!
* * *
## 源码
课程代码详见[GitHub仓库](https://github.com/akira-cn/graphics/tree/master/qcharts)
## 推荐阅读
[QCharts官网](https://www.qcharts.cn/#/home)