公众号:尤而小屋
作者:Peter
编辑:Peter

大家好,我是Peter~

本文介绍的是利用Plotly绘制一种相对少见的可视化图形:桑基图,这个图形可以说是展现数据流动的利器。

第一次接触桑基图的时候,是使用Pyehcarts(以后会专门介绍这个国产的可视化神器)绘制的,本文将介绍如何使用Plotly来实现这个图形。

手把手教你Plotly绘制桑基图!_父类

一、桑基图简介

手把手教你Plotly绘制桑基图!_数据_02

‍‍1.1 什么是桑基图

桑基图(Sankey diagram),即桑基能量分流图,也叫做桑基能量平衡图。它描述的是一组值到另一组值的流向,是一种特定类型的流向图。桑基,其实是一个人名,全名叫:马修·亨利·菲尼亚斯·里尔·桑基(Matthew Henry Phineas Riall Sankey),是一名爱尔兰裔工程师,也是英国皇家陆军工兵的上尉。

手把手教你Plotly绘制桑基图!_数据_03

在1898年的时候,他就使用这种图形来表示蒸汽机的能源效率, 在土木工程师学会会报纪要的一篇关于蒸汽机能源效率的文章中首次推出了第一个能量流动图,此后便以其名字命名为 Sankey 图,中文音译为桑基图:

手把手教你Plotly绘制桑基图!_父类_04

下图为1869年,查尔斯米纳德(Charles Minard)绘制的1812年拿破仑征俄图(Map of Napolean's Russian Campaign of 1812),这是一个在地图上覆盖桑基图的流程图,图形表示的是拿破仑军队进攻和撤退的军队力量对比:

手把手教你Plotly绘制桑基图!_父类_05

1.2 桑基图特点

桑基图主要的特点:

  1. 起始流量和结束流量是相同的,所有主支宽度和所有分出去的分支宽度总和是相等的,保持能量的守恒
  2. 在桑基图的内部,不同的线条代表了不同的流量分布情况,节点不同的宽度代表了特定状态下的流量大小

桑基图构成的3要素:节点、流量、边

手把手教你Plotly绘制桑基图!_子类_06

桑基图常用于能源、材料成分、金融等领域的可视化数据分析。本文最后会讲解一个实际的生活例子来说明桑基图的运用。

再看一个桑基图的例子:某国家地区的经济状况

手把手教你Plotly绘制桑基图!_父类_07

二、基础桑基图

下面的案例介绍的是基于plotly.graph_objects实现的基础桑基图:

手把手教你Plotly绘制桑基图!_父类_08


手把手教你Plotly绘制桑基图!_子类_09

解释一下上面的绘图代码,我们需要准备的数据有:

  • label:每个节点的名字,自己命名即可
  • soure:父节点,在plotly中是通过节点的索引来表示的,python中所用从0开始
  • target:数据流向的子节点
  • value:连接父节点和子节点的值

另外一种写法:

手把手教你Plotly绘制桑基图!_子类_10

三、基于json文件格式数据的桑基图

在plotly官网中有这样的一个例子:从给定的一个网站上下载json文件来绘制桑基图,分步骤来讲解下:

1、读取json文件并转成python字典数据

手把手教你Plotly绘制桑基图!_父类_11


手把手教你Plotly绘制桑基图!_父类_12

如何将字典格式的数据输出成json文件,并美化格式?

手把手教你Plotly绘制桑基图!_数据_13

美化后文件的大致格式(部分截图):

手把手教你Plotly绘制桑基图!_数据_14

手把手教你Plotly绘制桑基图!_数据_15

手把手教你Plotly绘制桑基图!_数据_16

还可以对图形的背景色进行设置:

手把手教你Plotly绘制桑基图!_父类_17


手把手教你Plotly绘制桑基图!_父类_18

四、特色桑基图

4.1 自定义位置的“桑基图”

在这里绘制的桑基图,是通过xy来自定义节点的位置:



手把手教你Plotly绘制桑基图!_父类_19

通过观察图形,整体画布的坐标原点应该是在左上角,横轴向右为正,纵轴向下为正。

4.2 自定义节点和边的颜色

通过color_mode和color_link参数能够自定义桑基图的节点和边的颜色:

手把手教你Plotly绘制桑基图!_数据_20


手把手教你Plotly绘制桑基图!_子类_21

五、桑基图_月度开销

下面通过小明一个月的总开支消费来讲解如何在实际数据中绘制桑基图。

1、首先我们看看小明整理的消费数据(虚拟数据)

小明同学的开支主要分为5大块:住宿、餐饮、交通、服装、红包,每个块中又分为各自的子块,以及对应的消费。

手把手教你Plotly绘制桑基图!_子类_22

2、整理数据,表明父级到子级的消费情况

因为桑基图的绘制是需要父级和子级节点之间的数据,所以我们需要先整体下数据:

下面的图形是5大主块的整理数据:

手把手教你Plotly绘制桑基图!_数据_23

下面的图形是各个子块对应的父级和子级数据整理:

手把手教你Plotly绘制桑基图!_子类_24

3、读取数据

然后将上面的两个数据放在一起,我们通过pandas读进来:

手把手教你Plotly绘制桑基图!_父类_25

4、找到数据的父类和子类中总共有多少个不同的元素,并进行索引的设置

将父类和子类的中元素全部加起来,再用集合set去重,找出全部的节点名称

手把手教你Plotly绘制桑基图!_子类_26


手把手教你Plotly绘制桑基图!_父类_27

接下来我们需要对每个节点进行索引的设置:

手把手教你Plotly绘制桑基图!_子类_28

将节点和索引进行字典形式的组合:

手把手教你Plotly绘制桑基图!_子类_29

分别根据父类节点和子类节点来生成对应的索引数据:

df["父类索引"] = df["父类"].map(index)
df["子类索引"] = df["子类"].map(index)
df

手把手教你Plotly绘制桑基图!_父类_30

终于看到了胜利的曙光,找到了我们需要绘图的数据:数据+父类索引+子类索引

手把手教你Plotly绘制桑基图!_父类_31


手把手教你Plotly绘制桑基图!_数据_32

看下最终的效果图:

手把手教你Plotly绘制桑基图!_父类_33



手把手教你Plotly绘制桑基图!_父类_34