来源:Python数据之道

作者:Peter

整理:Lemon

桑基图绘制实践

本文中介绍的是如何制作桑基图,使用的可视化库是强大的 Pyecharts (版本1.7.1,版本一致很重要)。文章将从如下几个方面进行介绍:

  • 什么是桑基图

  • 官网的两个 demo

  • 桑基图绘制项目实战

在开始之前,我们先来看看通过本文制作的最终效果图:

什么是桑基图?

桑基图(桑葚图),也叫桑基能量分流图或者桑基能量平衡图,里面的桑基其实是一个人名,全名是 马修·亨利·菲尼亚斯·里尔·桑基(Matthew Henry Phineas Riall Sankey) ,是一名爱尔兰裔工程师,也是英国皇家陆军工兵的上尉[1]

早在 1898 年的时候,他就使用这种图形来表示蒸汽机的能源效率:

桑基之后,桑基图逐渐成为科学和工程领域,代表平衡、能量流、物质流的标准模型,在一些产品的生命周期评估中也常被使用,通常应用于能源、材料成分、金融等数据的可视化分析。主要特点是:

  • 图形由边、流量和支点组成。边代表了流动的数据,流量代表了流动数据的具体数值,节点代表了不同分类

  • 始末端的分支宽度总和相等,即所有主支宽度的总和应与所有分出去的分支宽度的总和相等,保持能量的平衡。

官网demo

本文中使用的Pyecharts版本是1.7.1,版本的一致非常重要。

import pyecharts
pyecharts.__version__

demo_1

首先我们看看官网的第一个demo

from pyecharts import options as opts
from pyecharts.charts import Sankeynodes = [   # 所有节点名称{"name": "category1"},{"name": "category2"},{"name": "category3"},{"name": "category4"},{"name": "category5"},{"name": "category6"},
]links = [  # 每一条链路的数据,包含:父节点source + 子节点target + 数据值value{"source": "category1", "target": "category2", "value": 10},   {"source": "category2", "target": "category3", "value": 15},{"source": "category3", "target": "category4", "value": 20},{"source": "category5", "target": "category6", "value": 25},
]
c = (Sankey().add("sankey",nodes,links,linestyle_opt=opts.LineStyleOpts(opacity=0.2, curve=0.5, color="source"),label_opts=opts.LabelOpts(position="right"),).set_global_opts(title_opts=opts.TitleOpts(title="Sankey-基本示例"))#  .render("sankey_base.html")    生成HTML文件
)c.render_notebook()   # jupyter notebook中在线显示

在上面的代码中,nodes部分表示的是所有的节点名称,不管是父节点还是最小的子节点都要列出来links部分表示的是每条链路的数据,包含:父节点source + 子节点target + 数据值value。根据links的数据,我们可以发现:category1——-category2———category3———category4构成了一条完整的链路,category5—category6构成了另一条链路。

下面是最终的图形:

demo_2

接下来我们看看官网的第二个demo

import jsonfrom pyecharts import options as opts
from pyecharts.charts import Sankeywith open("product.json", "r", encoding="utf-8") as f:   # 导入json数据j = json.load(f)    # json数据转成字典数据
c = (Sankey().add("sankey",nodes=j["nodes"],   # 取出json数据的节点和链路数据links=j["links"],pos_top="10%",focus_node_adjacency=True,levels=[opts.SankeyLevelsOpts(depth=0,itemstyle_opts=opts.ItemStyleOpts(color="#fbb4ae"),linestyle_opts=opts.LineStyleOpts(color="source", opacity=0.6),),opts.SankeyLevelsOpts(depth=1,itemstyle_opts=opts.ItemStyleOpts(color="#b3cde3"),linestyle_opts=opts.LineStyleOpts(color="source", opacity=0.6),),opts.SankeyLevelsOpts(depth=2,itemstyle_opts=opts.ItemStyleOpts(color="#ccebc5"),linestyle_opts=opts.LineStyleOpts(color="source", opacity=0.6),),opts.SankeyLevelsOpts(depth=3,itemstyle_opts=opts.ItemStyleOpts(color="#decbe4"),linestyle_opts=opts.LineStyleOpts(color="source", opacity=0.6),),],linestyle_opt=opts.LineStyleOpts(curve=0.5),).set_global_opts(title_opts=opts.TitleOpts(title="Sankey-Level Settings"),tooltip_opts=opts.TooltipOpts(trigger="item", trigger_on="mousemove"),)
#    .render("sankey_with_level_setting.html")
)c.render_notebook()   # 在线显示
  • 读取本地的json数据,通过josn.load()读取转成Python字典

  • 取出json数据中的节点和链路数据进行绘图

桑基图绘制实践

原始数据整理

通过上面官网的例子我们明白了绘制桑基图需要的两个数据:节点数据+链路数据,下面????通过一个实际的案例来讲解如何生成绘制桑基图需要的数据

认识原始数据

Peter同学一个人在深圳搬砖,辛辛苦苦地搬了一个月,产生很多的开销????,这些开支主要分成5大块:

  • 住宿

  • 餐饮

  • 交通

  • 服装

  • 红包

每个部分又分别有不同的去向,所以这些数据就自然构成了一条条的链路,比如:总费用—住宿—房租(2000)总费用—交通—滴滴(220)等,我们只考虑两个节点之间的关系

分层级整理数据

1、接下来我们分不同的层级来整理原始数据,首先是第一层:总费用到5个子版块。算出每个子版块的总和

2、整理5个子版块的数据

3、我们将上面两个步骤得到的数据放入一个sheet中,命名为开支

桑基图数据生成

读取数据

首先我们将上面制作好的开支这份数据读到pandas中:

import pandas as pd
import numpy as np
import json# 等价于:data = pd.read_excel("life.xlsx",sheet_name=1)  1表示sheet_name的索引位置,索引从0开始
df = pd.read_excel("life.xlsx",sheet_name="开支")  # 直接写名字
df.head()

注意两点:

  1. 当一个表格中存在多个sheet的时候,我们需要指定sheet_name的名字

  2. 指定sheet_name的名字有两种方式:

    1. 直接指定名字

    2. 指定该sheet_name的位置索引

确定全部节点nodes

1、先找出全部的节点

所有的节点数据就是上面的父类和子类中去重后的元素,我们使用集合set进行去重,再转成列表

# 父类+子类中的数据,需要去重df['父类'].tolist()
df['子类'].tolist()

将上面的数据相加并且去重:

# 将两个列表相加,在转成集合set进行元素去重,再转成列表nodes = list(set(df['父类'].tolist() + df['子类'].tolist()))
nodes

2、生成节点数据

# 节点列表数据: nodes_listnodes_list = []
for i in nodes:dic = {}dic["name"] = inodes_list.append(dic)
nodes_list

生成链路数据

我们将导入的数据生成链路数据:每一行记录都是一个链路数据:

links_list = []for i in range(len(df)):dic = {}dic['source'] = df.iloc[i,0]  # 父类dic['target'] = df.iloc[i,1]  # 子类dic['value'] = int(df.iloc[i,2])   # 数据值 : 使用int函数直接强制转换,防止json.dump()报错links_list.append(dic)

Attention⚠️:导入的数据部分需要强制转换成int类型,防止后面的数据处理报错。

到此为止,我们已经完成了桑葚图中节点数据和链路数据的生成,下面开始绘图。

绘制桑基图

我们通过官网的2种不同方式来绘制桑基图

方式1

这种方式比较简单:直接将上面得到的nodes_listlinks_list整体放入绘图的代码中:

# 需要事先导入,否则jupyter notebook中可能不会出图
from pyecharts.globals import CurrentConfig, OnlineHostType
from pyecharts import options as opts  # 图形设置
from pyecharts.charts import Sankey    # 导入桑基图型的类nodes_list = [{'name': '围巾'},{'name': '长辈'},{'name': '网络费'},{'name': '服装'},{'name': '公交'},{'name': '同学'},{'name': '袜子'},{'name': '总费用'},{'name': '衣服'},{'name': '红包'},{'name': '交通'},{'name': '聚餐'},{'name': '滴滴'},{'name': '餐饮'},{'name': '管理费'},{'name': '水电'},{'name': '共享单车'},{'name': '外卖'},{'name': '房租'},{'name': '住宿'},{'name': '饮料'},{'name': '鞋子'},{'name': '地铁'}
]links_list = [{'source': '总费用', 'target': '住宿', 'value': 2580},{'source': '总费用', 'target': '餐饮', 'value': 1300},{'source': '总费用', 'target': '交通', 'value': 500},{'source': '总费用', 'target': '服装', 'value': 900},{'source': '总费用', 'target': '红包', 'value': 1300},{'source': '住宿', 'target': '房租', 'value': 2000},{'source': '住宿', 'target': '水电', 'value': 400},{'source': '住宿', 'target': '管理费', 'value': 100},{'source': '住宿', 'target': '网络费', 'value': 80},{'source': '餐饮', 'target': '外卖', 'value': 800},{'source': '餐饮', 'target': '聚餐', 'value': 300},{'source': '餐饮', 'target': '饮料', 'value': 200},{'source': '交通', 'target': '滴滴', 'value': 220},{'source': '交通', 'target': '地铁', 'value': 150},{'source': '交通', 'target': '公交', 'value': 80},{'source': '交通', 'target': '共享单车', 'value': 50},{'source': '服装', 'target': '衣服', 'value': 400},{'source': '服装', 'target': '鞋子', 'value': 300},{'source': '服装', 'target': '围巾', 'value': 150},{'source': '服装', 'target': '袜子', 'value': 50},{'source': '红包', 'target': '同学', 'value': 800},{'source': '红包', 'target': '长辈', 'value': 500}
]c = (Sankey().add("月度开支",nodes_list,links_list,linestyle_opt=opts.LineStyleOpts(opacity=0.5, curve=0.5, color="source"),label_opts=opts.LabelOpts(position="right"),).set_global_opts(title_opts=opts.TitleOpts(title="月度开支桑葚图"))
)c.render_notebook()

得到的桑基图在notebook中是动态的图形:

动态视频效果如下:

方式2

如果数据比较少,将nodes_listlinks_list放入绘图的代码中不会占据过多的空间;但是如果数据量大,不同链路种类多,全部放在整个绘图代码中,就会显得整个代码很臃肿。

于是产生了方式2:先将上面得到的nodes_listlinks_list生成一个json文件,再将json文件通过with方法读进来进行绘图。下面讲解如何通过得到的nodes_listlinks_list数据生成我们绘图需要的json数据。

json格式的数据,在python中以字符串的形式呈现,一定要用双引号括起来json模块中提供的4个功能:

  • dumpspython字典数据类型转成json数据类型的字符串

  • dump:字典数据转成字符串并且存储在文件中

  • loads:把json字符串转成字典数据类型

  • load:把文件打开,并且从字符串转换成字典数据类型

1、先生成字典数据

data_dic = {}data_dic["nodes"] = nodes_list
data_dic["links"] = links_list

得到的字典data_dic数据分为节点数据和链路数据,具体如下:

{'nodes': [{'name': '围巾'},   # 节点部分数据{'name': '长辈'},{'name': '网络费'},{'name': '服装'},{'name': '公交'},{'name': '同学'},{'name': '袜子'},{'name': '总费用'},{'name': '衣服'},{'name': '红包'},{'name': '交通'},{'name': '聚餐'},{'name': '滴滴'},{'name': '餐饮'},{'name': '管理费'},{'name': '水电'},{'name': '共享单车'},{'name': '外卖'},{'name': '房租'},{'name': '住宿'},{'name': '饮料'},{'name': '鞋子'},{'name': '地铁'}],'links': [{'source': '总费用', 'target': '住宿', 'value': 2580},  # 链路部分数据{'source': '总费用', 'target': '餐饮', 'value': 1300},{'source': '总费用', 'target': '交通', 'value': 500},{'source': '总费用', 'target': '服装', 'value': 900},{'source': '总费用', 'target': '红包', 'value': 1300},{'source': '住宿', 'target': '房租', 'value': 2000},{'source': '住宿', 'target': '水电', 'value': 400},{'source': '住宿', 'target': '管理费', 'value': 100},{'source': '住宿', 'target': '网络费', 'value': 80},{'source': '餐饮', 'target': '外卖', 'value': 800},{'source': '餐饮', 'target': '聚餐', 'value': 300},{'source': '餐饮', 'target': '饮料', 'value': 200},{'source': '交通', 'target': '滴滴', 'value': 220},{'source': '交通', 'target': '地铁', 'value': 150},{'source': '交通', 'target': '公交', 'value': 80},{'source': '交通', 'target': '共享单车', 'value': 50},{'source': '服装', 'target': '衣服', 'value': 400},{'source': '服装', 'target': '鞋子', 'value': 300},{'source': '服装', 'target': '围巾', 'value': 150},{'source': '服装', 'target': '袜子', 'value': 50},{'source': '红包', 'target': '同学', 'value': 800},{'source': '红包', 'target': '长辈', 'value': 500}]}

2、将生成的字典数据转成json数据,并保存到本地

通过json.dump方法将上面生成的字典类型数据转成json数据,并保存到本地:

with open("sankey.json","w",encoding="utf-8") as f:   # 数据保存到了本地# json.dump(data_dic, f)   写入一行数据json.dump(data_dic, f, indent=2, sort_keys=True, ensure_ascii=False)  # 写入多行数据

3、读取json数据进行绘图

import jsonfrom pyecharts import options as opts
from pyecharts.charts import Sankeywith open("sankey.json", "r", encoding="utf-8") as f:   # 1、打开保存的文件j = json.load(f)   # 2、json字符串转成字典类型数据
c = (Sankey().add("月度开支",nodes=j["nodes"],   # 3、通过键值对的映射关系来读取数据links=j["links"],pos_top="20%",focus_node_adjacency=True,levels=[opts.SankeyLevelsOpts(depth=0,
#                 itemstyle_opts=opts.ItemStyleOpts(color="#fbb4ae"),  4、属性的设置部分linestyle_opts=opts.LineStyleOpts(color="source", opacity=0.6),),opts.SankeyLevelsOpts(depth=1,
#                 itemstyle_opts=opts.ItemStyleOpts(color="#b3cde3"),linestyle_opts=opts.LineStyleOpts(color="source", opacity=0.6),),opts.SankeyLevelsOpts(depth=2,
#                 itemstyle_opts=opts.ItemStyleOpts(color="#ccebc5"),linestyle_opts=opts.LineStyleOpts(color="source", opacity=0.6),),],linestyle_opt=opts.LineStyleOpts(curve=0.5,color="source",opacity=0.6,type_="dotted"),label_opts=opts.LabelOpts(position="right")).set_global_opts(title_opts=opts.TitleOpts(title="月度开支桑葚图"),tooltip_opts=opts.TooltipOpts(trigger="item", trigger_on="mousemove|click",is_show=True),)
)c.render_notebook()

看看实际的动态化效果:

参考资料

[1]

桑基的介绍: https://zhuanlan.zhihu.com/p/127360262

作者简介

Peter,硕士毕业僧一枚,从电子专业自学Python入门数据行业,擅长数据分析及可视化。喜欢数据,坚持跑步,热爱阅读,乐观生活。个人格言:不浮于世,不负于己

个人站点:www.renpeter.cn,欢迎常来小屋逛逛

---------End---------


往期精彩回顾适合初学者入门人工智能的路线及资料下载机器学习及深度学习笔记等资料打印机器学习在线手册深度学习笔记专辑《统计学习方法》的代码复现专辑
AI基础下载机器学习的数学基础专辑
获取本站知识星球优惠券,复制链接直接打开:
https://t.zsxq.com/qFiUFMV
本站qq群704220115。加入微信群请扫码:

【Python基础】用Python制作漂亮的流动桑基图相关推荐

  1. 炫酷!用Python制作漂亮的流动桑基图

    作者:Peter 整理:Lemon 桑基图绘制实践 本文中介绍的是如何制作桑基图,使用的可视化库是强大的 Pyecharts (版本1.7.1,版本一致很重要).文章将从如下几个方面进行介绍: 什么是 ...

  2. python怎么画函数图_可视化|Python绘制桑基图

    桑基图(Sankey diagram),即桑基能量分流图,也叫桑基能量平衡图.它是一种特定类型的流程图,图中延伸的分支的宽度对应数据流量的大小,通常应用于电商.材料成分.金融等数据的可视化分析.因18 ...

  3. 数据治理 Python桑基图处理表关系

    数据治理 Python桑基图处理表关系 需求 随着hive库表越来越多,调度出问题后,排查时间越来越长.计划通过桑基图以及血缘图谱解决,当前先用桑基图页面顶一段时间.后期做成web服务,如果有可能,尽 ...

  4. 【Python 实战基础】如何绘制桑基图分析人口流动和年龄数据

    目录 一.实战场景 二.主要知识点 文件读写 基础语法 字符串处理 文件生成 数据构建 三.菜鸟实战 1.创建 python 文件 2.运行结果 一.实战场景 实战场景:如何绘制桑基图分析人口流动和年 ...

  5. python制作桑基图_Python制作桑基图

    这张图是不是十分的漂亮呢?今天偶然间看到一张十分炫酷的图,深深地被它妖艳炫酷的的外表所迷惑,查了许多资料之后,得知它叫"桑基图".古人云:"桑吉桑吉,高贵美丽" ...

  6. python 桑基图 地理坐标_利用Python+Excel制作桑基(Sankey)图

    利用Python+Excel制作桑基(Sankey)图 桑基图的简介 桑基图 (Sankey  Diagram),是一种特定类型的流图,用于描述一组值到另一组值的流向.下图为1869年,查尔斯米纳德( ...

  7. 二十一. Python基础(21)--Python基础(21)

    二十一. Python基础(21)--Python基础(21) 1 ● 类的命名空间 #对于类的静态属性:     #类.属性: 调用的就是类中的属性     #对象.属性: 先从自己的内存空间里找名 ...

  8. Python基础学习-Python中最常见括号()、[]、{}的区别 2015-08-13 07:54 by xuxiaoxiaoxiaolu, 1138 阅读, 0 评论, 收藏, 编辑 Pytho

    Python基础学习-Python中最常见括号().[].{}的区别 2015-08-13 07:54 by xuxiaoxiaoxiaolu, 1138 阅读, 0 评论, 收藏, 编辑 Pytho ...

  9. python基础类型,Python基础-类

    Python基础-类 @(Python)[python, python基础] 写在前面 如非特别说明,下文均基于Python3 摘要 本文重点讲述如何创建和使用Python类,绑定方法与非绑定方法的区 ...

最新文章

  1. JAVA增删改查XML文件
  2. 六大“未来式”存储器,谁将脱颖而出?
  3. IDEA 13 tomcat 进行远程调试
  4. 2017乌鲁木齐区域赛I(带权并查集)
  5. python程序员在公司都是做什么的-程序员是做什么的?工资待遇怎么样?
  6. 结构体+字符串比较大小(HDU1234开门人和关门人 )
  7. 2020年快手校招JAVA岗笔试第一题
  8. SecureCRT如何进入和退出全屏及调出菜单栏
  9. 西门子和阿里云要搞啥事情?| 极客头条
  10. 异步fifo_异步FIFO
  11. linux 审计工具auditd日志audit.log时间戳转换查看
  12. 实例详解机器学习如何解决问题
  13. 遥感软件_遥感软件的可持续发展
  14. JavaScript简单计算器
  15. 思科1242 AP无法连接到无线控制器
  16. 计算机电子表格编辑栏,怎么在Excel中添加开发工具到工具栏
  17. K210 Draco AI环境的搭建(1)
  18. 聊聊龙芯Java开源
  19. python是跨平台语言吗_python可以跨平台么
  20. WRP Ultimate 3D Endless Runner Kit - 狂徒末路跑酷角色替换教程

热门文章

  1. postgresql数据库连接池pgbouncer的搭建
  2. iOS用户设计指南-特别说明
  3. OpenFlow: Enabling Innovation in Campus Networks
  4. HBuilderX 连接电脑的模拟器问题
  5. Gym-101466K Random Numbers(线段树,数学,唯一分解定理)
  6. python_45_目录编程
  7. Linux及安全课程——相关链接总结
  8. ASP.NET Page执行顺序如:OnPreInit()、OnInit()
  9. windows进程通信 -- WM_COPYDATA消息
  10. 流行病学数据分析基础:识别和控制混杂