Appearance
分析-表格带你浅尝数据分析
从这一章开始我们将进入分析部分,这部分我们将用 Sparrow 去可视化数据,去解决下面提出的问题:
- 揭露中世纪是一个压抑的时代。(展现时代和哲学家数量的相关性和异常值)。
- 证明哲学家谈论的都是一些永恒的问题。(展现哲学家观点的特征)。
- 哲学家,哲学问题和流派的数量关系是怎样的?(展现哲学家,哲学问题和流派的数量的特征)。
- 哲学问题,哲学家和流派的数量随着时间是如何变化的?(探索哲学问题,哲学家,流派的数量的趋势,以及它们和时间的依赖关系)。
- 有多少哲学家的寿命超过了40岁,其中年龄最大和最小分别是多少?(探索哲学家寿命的分布和极值)。
- 每个哲学家回答了哪些问题?每个问题有哪些哲学家回答了?哲学家和问题之间的关系又如何?(探索哲学家和问题——网络数据之间的拓扑结构)。
- 每个流派有哪些哲学家?哪个流派的哲学家比较多?哪个比较少?(探索哲学家和流派——网络数据之间的拓扑结构)。
- 哲学的中心是怎么变化的的?(哲学家聚集地点——几何数据的形状)。
一般来说,在真正设计我们的可视化之前,我们大概看一下数据,然后再确定可视化设计。了解数据最基础的手段就是通过表格,那么这一章我们就用表格先去了解一下 philosophers.json 这份数据。
Sparrow
首先我们用我们的 Sparrow 去绘制表格,这里的思路就是用 Text 几何元素去绘制。具体的代码和效果如下:
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 keys = ["id", "name", "country", "lifespan", "points"];
return sp.plot({
data,
type: "layer",
transforms: [
(data) => data.filter((_, i) => i < 10),
// 预处理数据
(data) =>
data.map(({ lifespan, points, ...rest }) => ({
...rest,
lifespan: `[${lifespan[0]}, ${lifespan[1]}]`,
points: `[${points
.slice(0, 2)
.map((d) => d.slice(0, 2) + "...")
.join(", ")}]`,
})),
// 转换成一系列文本
(data) => {
const ths = ["index", ...keys].map((key) => ({
index: -1,
key,
value: key,
header: true,
}));
const tds = data.flatMap((d, i) => {
const cell = keys.map((key) => ({ index: i, key, value: d[key] }));
return [...cell, { index: i, key: "index", value: i }];
});
return [...ths, ...tds].reverse();
},
],
scales: {
y: { type: "band", padding: 0 },
x: { domain: ["index", ...keys], padding: 0 },
fontWeight: { type: "identity" },
},
width: 900,
guides: {
x: { display: false },
y: { display: false },
},
encodings: {
y: "index",
x: "key",
},
children: [
{ // 背景的表格
type: "cell",
encodings: {
fill: "none",
stroke: "#eee",
},
},
{ // 前面的文字
type: "text",
paddingTop: 10,
paddingLeft: 10,
paddingBottom: 10,
encodings: {
text: (d) => (d.header ? d.value.toUpperCase() : d.value),
fontWeight: (d) => (d.header ? "bold" : "normal"),
},
styles: {
dx: "0.5em",
dy: "-1em",
},
},
],
});
})();
可以发现 Sparrow 确实也可以绘制简单的表格,但是也存在不少问题:
- 容器高度不能根据行数自适应,所以只能绘制少部分数据。
- 不具有交互功能:不能改变每个行或者列的占比。
- 绘制交叉表会比较麻烦,只适合绘制非嵌套的数据。
那么接下来我们就试试 AntV 的 S2,去绘制相同的数据,并且找出哲学家观点关键词的信息洞察。
任务分析
我们先来分析一下“找出哲学家观点关键词的信息洞察” 这个任务。
这个任务需要分析哲学和问题之间的关系,分成几步:
- 罗列出哲学家信息和观点,然后将观点进行分词,获得所有关键词以及出现频次,获得要分析的数据。
- 基于原生数据进行基本数据分析,确定数据间的基本关联关系。
- 使用表可视化分析引擎 [S2] 绘制多维分析表。
- 为了更加直观看出哲学家思考方向的差异性,我们使用不同背景颜色表现哲学家关键词的权重占比。
- 获取数据洞察。
可视分析
数据处理
首先我们处理一下 philosophers.json 这里的数据,获取所有哲学家的观点列表,以及观点中的关键字所占词频,然后使用社区开源的 nodejieba 进行关键词抽取。
数据分析
在进行数据分析之前,我们先来了解几个简单的基本概念:
- 度量(指标):数值本身,比如价格、数量等。
- 维度:可以理解为分析数据的角度,比如省份、类型等。
通过观察一条样例数据,我们可以得到「国家」、「姓名」、「出生年份」、 「逝世年份」、 「观点」、「关键词」六个维度,以及关键词 「权重」这个指标。我们可以根据「国家」将哲学家进行分组,而每个哲学家提出的观点又可以按照 「关键词」进行拆分,每个关键词用 「权重」数据去描述。这样一来我们就可以按照以下看数思路进行分析:
- 查看一个国家有哪些哲学家
- 每个哲学家的基本信息有哪些
- 哲学家分别提出了怎样的观点
- 每个观点对应的关键词有哪些
用 S2 绘制多维分析表
在绘制多维分析表之前,我们需要了解 S2 是怎么绘制多维分析表的。S2 是数据驱动的表可视分析引擎。通过 schema
指定行列维度和指标的位置,结合配置项设置相关分析属性,将明细数据和行列维度关联起来。从代码层面来反应整体分为三步:
- 数据准备
- 配置项准备
- 渲染
1、数据准备
- 首先将原始数据按照「关键词」维度进行拆分,将数据打平,得到如下 S2 可以直接消费的明细数据:
(全量数据)
json
[
{
"points": "水是万物之源。",
"name": "泰利斯",
"country": "希腊",
"word": "水是",
"weight": 11.739204307083542,
"start": -624,
"end": -546
},
{
"points": "水是万物之源。",
"name": "泰利斯",
"country": "希腊",
"word": "万物",
"weight": 7.75434839431,
"start": -624,
"end": -546
},
{
"points": "水是万物之源。",
"name": "泰利斯",
"country": "希腊",
"word": "之源",
"weight": 9.23723855786,
"start": -624,
"end": -546
},
...
」
- 将维度按照数据分析确定的维度顺序依次放置在行维度位置,并指定「频率」为指标。
const s2DataConfig = {
fields: { // 维度指标配置项
rows: ['country', 'name', 'start', 'end', 'points', 'word'], // 行头
columns: [], // 列头
values: ['weight'], // 数值
},
meta: [ // 对各个维度指标进行别名、格式化配置
{
field: 'word', // 维度
name: '关键词', // 别名
},
{
field: 'points',
name: '观点',
},
{
field: 'name',
name: '姓名',
},
{
field: 'country',
name: '国家',
},
{
field: 'start',
name: '出生',
formatter: getFormatter, // 格式化方法
},
{
field: 'end',
name: '逝世',
formatter: getFormatter,
},
{
field: 'weight',
name: '权重',
formatter: (val) => val.toFixed(2),
},
],
data, // 可从 GitHub 中下载
};
const getFormatter = (val) => {
if (val < 0) {
return `公元前${replace(val, '-', '')}年`;
} else {
return `${val}年`;
}
};
2. 配置项准备
这一步除了基本表体展示交互相关的基础配置项,主要做的就是文本和单元格背景颜色通道映射的配置。 首先准备一个色板如下:
json
const PALETTE_COLORS = [
'#B8E1FF',
'#9AC5FF',
'#7DAAFF',
'#5B8FF9',
'#3D76DD',
'#085EC0',
'#0047A5',
'#00318A',
'#001D70',
];
计算得到该组数据的最大最小值:
json
import { max, min} from 'lodash';
const weights = data.map((item) => item.weight);
const maxWeight = max(weights);
const minWeight = min(weights);
const weightSpan = maxWeight - minWeight;
将数值区间和颜色区间进行关联,并将权重数值映射到对应颜色区间:
const s2Options = {
width: '',
height: 400,
conditions: { // 字段标记
text: [ // 权重(weight)大于 20 的数值展示为白色
{
field: 'weight',
mapping(value) {
if (value >= 20) {
return {
fill: '#fff',
};
}
},
},
],
background: [ // 权重(weight)在不同的范围,展示不同的背景色
{
field: 'weight',
mapping(value) {
let backgroundColor;
const colorIndex =
Math.floor(
(((value - minWeight) / weightSpan) * 100) /
PALETTE_COLORS.length,
) - 1;
if (colorIndex <= 0) {
backgroundColor = PALETTE_COLORS[0];
} else if (colorIndex >= PALETTE_COLORS.length) {
backgroundColor = PALETTE_COLORS[PALETTE_COLORS.length - 1];
} else {
backgroundColor = PALETTE_COLORS[colorIndex];
}
return {
fill: backgroundColor,
};
},
},
],
},
interaction: { // 关闭部分交互操作
selectedCellsSpotlight: false,
hoverHighlight: false,
},
};
3. 渲染
有了前面的准备工作,绘制多维分析表就很容易了。
ReactDOM.render(
<SheetComponent
dataCfg={s2DataConfig}
options={s2Options}
adaptive={true}
header={{
title: '哲学家的观点',
extra: [<PaletteLegend />],
}}
onDataCellMouseUp={onDataCellMouseUp}
/>,
document.getElementById('container'),
);
最后绘制出来的效果如下图所示,也可以打开我们部署好的 线上示例
数据洞察
从中我们可以获取一些有效信息和洞察:
- 每个国家有那些哲学家
- 每个哲学家的详细信息
- 哲学家每个关键词的权重占比
拓展
透视表
在统计学中,透视表是矩阵格式的一种表格,显示多变量频率分布。它们提供了两个变量(或者多个)之间的相互关系的基本画面,可以帮助发现它们之间的相互作用,帮助业务进行交叉探索分析,是目前商业 BI
分析领域中使用频率最高的图表之一。
S2
S2
是 AntV
团队推出的数据表可视化引擎,是多维交叉分析领域的表格解决方案,数据驱动视图,提供底层核心库、基础组件库、业务场景库,具备自由扩展的能力,让开发者既能开箱即用,也能基于自身场景自由发挥。
小结
这一章带大家用表格去展示和认识数据,表格其实也算一种古老的数据可视化方法。再一次认识数据之后,接下来我们就正式进入分析环节!