Appearance
分析-哲学家之间在讨论啥“八卦”?
本章我们将会使用可视化工具来分析苏菲的世界中,哲学家都在讨论什么八卦内容?这个问题最终可以抽象成“苏菲的世界中,涉及到的问题和回答的关键词是什么?”
任务分析
这个问题需要分析哲学问题和回答的关键词,分成几步:
- 罗列出问题和回答,然后将问题和回答进行分词,获得所有关键词以及出现频次,获得要分析的数据
- 使用词云图对关键词数据进行可视化,配合字体大小、颜色和频次关联,获得更好的视觉效果
- 获取数据洞察
下面会按照这几个步骤,去实现这样的一个词云图,并从中获得数据洞察。
数据处理
首先我们从《苏菲的世界》中,获取所有问题列表,以及所有的回答列表,然后使用社区开源的 nodejieba 进行关键词抽取。
javascript
const nodejieba = require('nodejieba');
const topN = 5;
const keywords = nodejieba.extract('世界上根本没有真正的变化,没有任何事物可以变成另外一种事物。', topN);
这里就不去过多介绍了,最后获取的关键词数据放在 GitHub 上(问题、回答观点)。
用 Sparrow 绘制词云
所谓的词云就是将关键字互不重叠的在画布上排列展示,文字的大小和词云的关键程度呈正相关。
因为 Sparrow 本身没有实现词云布局算法,所以这里我们将先使用 d3-cloud 去对词语进行布局,然后再用 Sparrow 去绘制。
javascript
(async () => {
// 获得数据
const response = await fetch(
"https://gw.alipayobjects.com/os/bmw-prod/d345d2d7-a35d-4d27-af92-4982b3e6b213.json"
);
const data = await response.json();
// 获得词语列表
const words = data.flatMap((d) =>
d.words.map(({ weight, word }) => ({
value: weight,
text: word,
name: d.name,
}))
);
// 用 d3-cloud 对文字进行布局
const width = 640;
const height = 480;
const computedWords = await new Promise((resolve) =>
d3.layout
.cloud()
.size([width, height])
.words(words)
.padding(2)
.rotate(() => ~~(Math.random() * 2) * 90) // 文字的旋转角度
.fontSize((d) => d.value * 2) // 字号
.on("end", resolve) // 计算布局完成的回掉函数
.start() // 开始进行布局
);
// 绘制处理好的文字
return sp.plot({
data: computedWords,
type: "text",
width,
height,
paddingTop: 0,
paddingBottom: 0,
paddingLeft: 0,
paddingRight: 0,
guides: {
x: { display: false },
y: { display: false },
color: { display: false },
},
scales: {
fontSize: { type: "identity" },
rotate: { type: "identity" },
y: { range: [0, 1] }, // 默认的 range 是 [1, 0]
},
encodings: {
x: "x",
y: "y",
rotate: "rotate",
fontSize: "size",
text: "text",
fill: "name",
},
styles: {
textAnchor: "middle",
},
});
})();
一个简单的词云图就绘制完成了,但是这个地方存在一个问题,就是不能通过我们那的可视化结果去判断每一个词是哪一个哲学家说的。
如果当鼠标移动到任意一个词上,会出现一个提示信息提示该词的来源就好了。可惜 Sparrow 不支持交互,所以接下来我们 AntV 的 G2 再来绘制一下词云。
用 G2 绘制词云
在用 G2 绘制词云图之前,我们需要了解 G2 是怎么理解和绘制图表的。G2 是基于图形语法的统计图表引擎,它通过:
- 不同的图元绘制不同类型的可视化图表。
- 通过数据映射,将数据和图元的视觉属性绑定起来,比如:color、size、shape 等等。
图元 Geometry | 视觉属性通道 |
---|---|
那么在绘制词云图的时候,我们使用 point
这个图元去绘制文字,然后文字的位置、大小、颜色都是和数据绑定的。所以绘制词云图分成为 3 个步骤。
- 视觉属性映射
将原始关键词的权重数据,通过数据转化,变化成文字的位置、大小、颜色信息,以便于下一步 G2 的绘制。
javascript
import DataSet from '@antv/data-set';
const dv = new DataSet().createView().source(data);
const fontSize = new Scale.Linear({
range: [24, 70],
domain: dv.range('value')
});
const [min, max] = dv.range('value');
dv.transform({
type: 'tag-cloud',
fields: ['word', 'value'],
size: [800, 600], // 画布的大小
font: 'Verdana',
padding: 0,
// 映射旋转角度
rotate() {
let random = ~~(Math.random() * 4) % 4;
if (random === 2) {
random = 0;
}
return random * 90; // 0, 90, 270
},
// 映射字体大小,范围为 [24px, 70px]
fontSize(d) {
return d.value ? 24 + (70 - 24) / (max - min);
}
});
这里用到了 dat-set 模块中的 tag-cloud
的数据处理函数,对数据进行词云图布局和视觉通道的映射,获得的数据结构为:
javascript
[{
value: 26.6063055869,
title: "祁克果",
word: "阶段",
font: "Verdana",
style: "normal",
weight: "normal",
rotate: 270,
size: 70,
x: 286,
y: 188
}]
这个数据结构中,已经包含有文字渲染需要的所有信息,直接拿这个信息在 G2 中绘制即可。
- 定制词云图需要的图元 shape
javascript
import { Chart, registerShape } from '@antv/g2';
// 准备图形:给 point 图元注册一个词云形状 cloud
registerShape('point', 'cloud', {
draw(cfg, container) {
// 从数据中获取视觉属性
const attrs = {
...cfg.defaultStyle,
...cfg.style,
textAlign: 'center',
textBaseline: 'Alphabetic',
// 数据映射属性
fontSize: cfg.data.size,
text: cfg.data.text,
fontFamily: cfg.data.font,
fill: cfg.color,
}
const textShape = container.addShape('text', {
attrs: {
...attrs,
x: cfg.x,
y: cfg.y
}
});
// 旋转图形
if (cfg.data.rotate) {
Util.rotate(textShape, (cfg.data.rotate * Math.PI) / 180);
}
return textShape;
}
});
通过这个方式,在 G2 的图形池中,就增加了 cloud
类型的绘图,并且可以从数据中去指定 fontSize
、text
、fontFamily
、fill
、rotate
等属性。
- 使用 G2 绘制词云图
有了前面的准备工作,绘制词云图就很容易了。
javascript
import { Chart } from '@antv/g2';
// 1. 创建图表容器
const chart = new Chart({
container,
autoFit: false,
width: 800,
height: 600,
padding: 0
});
// 2. 装载第一步中处理好的数据
chart.data(data);
chart.legend(false);
chart.axis(false);
chart.tooltip({
showTitle: false,
showMarkers: false
});
chart.coordinate().reflect();
// 3. 使用 point 中的 cloud 形状,并将位置、颜色等视觉属性绑定到数据字段上
chart
.point()
.position('x*y')
.color('title', colors)
.shape('cloud')
.tooltip('title');
chart.render();
最后绘制出来的效果如下图所示,也可以打开我们部署好的线上示例。
问题关键词分析 | 观点关键词分析 |
---|---|
数据洞察
从中我们可以获取一些有效信息和洞察:
- 人们更加关注上帝、世界的起源、理性与感性的关系。
- 许多人试图回答来自大自然、心灵或精神的问题。
- 马克思和笛卡尔对人类历史有着巨大的影响。
拓展
图形语法(Grammar of Graphics)
首先我们理解什么是语法?就像我们 JavaScript 语法一样,通过一些正交的关键字,以及语法规范,可以让我们基于语法写出各种各样超越想象的应用。图形语法,顾名思义就是描述视觉图形的语法规范。
人的眼睛(视觉)无论是在获信息的效率上,还是在获取信息的量级上,都是远高于其他的方式,比如听觉、触觉、嗅觉等等。而对于视觉,人脑处理不同视觉属性的速度不同,一般来说对于颜色最敏感,其次是大小,然后是形状,等等。
数据可视化分析是建立在以上的客观的生理条件上的,因此基于图形语法的可视化引擎(ggplot2、vega、G2),大多会将一个可视化图表分成几大部分:
- 图形:不同的图形包含有不同的数据分析方法,比如折线一般用于看数据趋势,柱形一般用于进行数据的对比,点一般用于看分布,等等。
- 视觉属性通道:通过将数据和视觉属性绑定和映射,让人脑更容易从视觉上获取数据上的有效的信息。
- 坐标系:代表数据大小的图形区块,在画布中的展示方式不同,可能带来具有不同的分析效率。
而上述的这些内容,社区上几乎所有的图表库多多少少都会涉及一些。比如在 d3 的代码中,经常遇到类似以下:
javascript
d3.selectAll("p").style("color", function() {
return "hsl(" + Math.random() * 360 + ",100%,50%)";
});
这个过程实际就是通过回调的方式,将颜色这个视觉属性和数据绑定起来。
G2
G2 是 AntV 旗下的第一个数据可视化引擎,基于图形语法,专注于统计图表和数据分析。它的取名就是来自于 Grammar of Graphics 首字母,也是国内首个基于图形语法的统计图表引擎。这类图表引擎的优势和缺点都很明显。
- 优势:更贴近数据分析语义、变幻出各种各样的可视化图表、灵活性非常高
- 劣势:概念多,上手门槛高、实现业务需求还需要不少代码量
如果想了解更多,可以去阅读 G2 官网教程,未来我们将在 G2 图形语法的基础上,优化交互语法,增加动画语法,大大提升 G2 在实时数据、可视化叙事上的表达能力,并将会发表的学术论文,更详细体系的介绍它的技术模块和架构。
小结
这一章,我们通过数据分析的过程,去了解苏菲的世界中,哲学家讨论的一些话题情况。通过这个过程,可以大概了解文本可视化的基本概念。
另外我们拓展讲了一些关于 G2 和图形语法的知识,现代的可视化图表技术,一般都离不开图形语法,理解这个就可以理解大部分的可视化内库,同样,上述的文本可视化,也是在图形语法基础上延伸出来的一个分支。在实际业务场景中,有大量的数据看板,会使用指标卡、富文本、词云图等去表达实际的业务含义。
下一节,我们会用类似的方式,去介绍去如何实现关系数据可视化和分析。