你好,我是月影。
图表是我们在可视化中展示数据常用的方式之一,常见的有柱状图、折线图、饼图等等,我们之前也都一一实现过。如果我让你总结一下图表的实现方法,你能说出来吗?总结不出来也没关系,这节课,我们就一起梳理一下实际项目中常用的制图方法。
实现图表有两种方式,一是使用现成的图表库,二是使用数据驱动框架,前者胜在简单易用,后者则更加灵活。所以,我们会用两节课的时间分别来讲,使用图表库和使用数据驱动框架的制图思路。
因为使用图表库更加简单,所以这一节课我们先来讲怎么使用它实现图表。另外,这节课的实践性比较强,我建议你跟着我的讲解一块儿动手去写,这样能更快地理解课程的内容。
说了这么多,我们今天到底会用哪些图表库呢?我们主要会使用我们比较熟悉的SpriteJS和QCharts来绘制图表。其他的图表库,例如更常用的ECharts,它在图表实现上的原理和用法和我们今天讲的基本相同,所以学完了今天的内容,你完全可以根据它的教程文档去自学。
好了,确定了要使用的工具之后,我们就要配置和加载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,你可以fork这个项目,按照其中的配置项来设置。当加载完成之后,我们就可以开始绘制基础的图表了。
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]);
这样,我们就让图形显示出来了,效果如下:
了解了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对象的子元素中去,就可以将坐标轴显示出来。
添加完坐标轴之后,我们再添加图例(Legend)和提示(Tooltip)。最简单的方式,是我们直接创建两个对象,然后将它们添加到charts对象的子元素中。
const legend = new Legend();
const tooltip = new Tooltip();
chart.append([line, axisBottom, axisLeft, legend, tooltip]);
添加了图例和提示信息后的图表如下图所示:
接下来,我们再来说说怎么用QCharts绘制面积图和柱状图。
学会了折线图的绘制方法,我们就可以用相同的思路非常简单地绘制其他图形了,比如说,我们可以简单将Line对象换成Area或Bar对象,这样就可以用同样的数据绘制出面积图或柱状图。这是为什么呢?
因为像折线图、面积图、柱状图这些表现形式不同的图表,它们能够接受同样格式的数据,只是想要侧重表达的信息不同而已。一般来说,折线图强调数据变化趋势,柱状图强调数据的量和差值,而面积图同时强调数据量和变化趋势。在实际项目中,我们要根据不同的需求选择不同的基本图形。
如果要强调整体和局部比例,我们还可以选择绘制饼图。与折线图、面积图、柱状图相比,饼图不用配置图例,因为图例会自动生成,而且饼图也不需要坐标轴,所以使用起来更加简单。
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]);
上面的代码用同样的数据绘制了一个饼图。
讲完了这些常见的基础图表,我们再来看几个比较有趣的图表。
首先是雷达图,它一般用来绘制多组固定数量的数据,可以比较直观地显示出这组数据的特点。我们经常会在游戏中看见它的应用,比如,下面这张图表就显示了三国武将诸葛亮的各方面数据。
雷达图的实现代码如下:
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]);
除此之外,还有一些其他类型的图表,可以用来展示特殊的信息。比较典型的有仪表盘,它可以显示某个变量的进度。
仪表盘实现代码比较简单,如下所示:
const gauge = new Gauge({
min: 0,
max: 100,
percent:60,
lineWidth: 20,
tickStep: 10
});
gauge.style('title', { fontSize: 36 });
chart.append(gauge);
如果我们要显示多个变量的进度,还可以使用玉玦图。
对应的代码也非常简单,如下所示:
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节课已经说过,这里就不再多说了。如果你还不熟悉,可以再去回顾一下。
我们刚才讲的都是在图表上绘制单一变量,那要想在图表上聚合多元变量,比如在一张天气图表上同时展示降水量、气温,我们可以同时绘制多个图形来表示。
我们可以分两种情况来讨论,最简单的情况是用相同的图形来绘制不同的变量。这个时候,我们可以直接通过不同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 });
...
如果我们想用不同类型的图形来展示多个变量,在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子元素列表里,就可以将两个不同类型的图形显示出来了,具体的效果如下:
这节课,我们主要学习了QCharts图表库的使用。
QCharts是基于SpriteJS的简单可视化图表库,我们通过它可以绘制各种类型的图表。一般来说,我们是先创建图表对象,然后绑定数据,接着添加图形对象以及其他的插件,包括图例和提示。通过将图形和插件以子元素的形式添加到图表对象上,就能把图表内容最终显示出来了。
我们可以很方便地根据数据特点和业务需要,用数据绘制折线图、面积图、柱状图、饼图、雷达图等图表,还可以绘制特殊的仪表盘和玉玦图。另外,如果要显示多维数据,我们也可以用稍微复杂一些的南丁格尔玫瑰图。
最后,我们还可以把多维度变量聚合在一个图表中来显示不同的图形组合。具体操作是,我们先筛选数据,然后创建不同类型的图形对象,最后将它们都添加到图表对象的子元素中。
总的来说,我们今天讲的其实都是QCharts图表库,最基础、最常用的方法。QCharts还提供了众多其他类型的图表,以及灵活操作图表样式的API。如果你有兴趣继续钻研,可以通过我课后给出的参考链接进一步学习。
你学会了使用不同图表来表达不同数据了吗?你可以试着使用GitHub仓库里的北京市天气数据和空气质量数据,实现一个温度、湿度、风速、空气质量的聚合图表吗?如果用来展示温度、湿度、风速和空气质量的图形都不相同就更好了。
这些常用的制图方法你都学会了吗?下节课,我们会接着来学,怎么使用数据驱动框架来表达不同数据,挑战才刚刚开始呢,我们下节课再见!
课程代码详见GitHub仓库
评论