桑基图(Sankey):定向追踪你的数据

小郭叨叨叨
上年发现运来兄有介绍桑基图,感觉好玩,但不知如何使用,无学习的欲望;最近发现果子大神也在介绍桑基图,又勾起了我的好奇心,偷闲学习了一下有关桑基图的R包,特此搬砖备用,以待不时之需😂

What's that ?

桑基图: 流图 ( flow diagram ) 的一种,用来描述流动情况,图中延伸的分支的宽度对应数据流量的大小,通常应用于能源、材料成分、金融等数据的可视化分析。最早由爱尔兰人Matthew Henry Phineas Riall Sanke提出。Sankey是一名船长也是工程师,1898年Sankey在土木工程师学会会报纪要的一篇关于蒸汽机能源效率的文章中首次推出了第一个能量流动图,后来被命名为Sankey图,中文音译为桑基图。

R语言中实现方式

ggalluvial packages;
ggforce packages.

本文档仅仅为了学习桑基图相关R包的使用方法,因手上缺少实际数据,不再展开

1. ggalluvial : Alluvial Diagrams in ggplot2

准备工作
install.packages("ggalluvial")
library(ggalluvial)
##使用vignette查看演示教程
vignette(topic = "ggalluvial", package = "ggalluvial")
Alluvial data

ggalluvial识别两种形式的数据:分类重复测量数据的“wide”和“long” formats。用于存储多个分类维度的数据类型"tabular(or array)"也很受欢迎,如TitanicUCBAdmissions数据集。
为了和ggplot2数据格式保持一致,ggalluvial不接受表格输入;base::data.frame()可将这些array 转换成可接受的 data frame.

简单的例子
> head(as.data.frame(UCBAdmissions), n = 12)
      Admit Gender Dept Freq
1  Admitted   Male    A  512
2  Rejected   Male    A  313
3  Admitted Female    A   89
4  Rejected Female    A   19
5  Admitted   Male    B  353
6  Rejected   Male    B  207
7  Admitted Female    B   17
8  Rejected Female    B    8
9  Admitted   Male    C  120
10 Rejected   Male    C  205
11 Admitted Female    C  202
12 Rejected Female    C  391
> is_alluvia_form(as.data.frame(UCBAdmissions), axes = 1:3, silent = TRUE)
[1] TRUE
ggplot(as.data.frame(UCBAdmissions),
       aes(y = Freq, axis1 = Gender, axis2 = Dept)) +
  geom_alluvium(aes(fill = Admit), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", label.strata = TRUE) +
  scale_x_discrete(limits = c("Gender", "Dept"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("UC Berkeley admissions and rejections, by sex and department")
Alluvia format :wide & long

wide格式:as.data.frame

ggplot(as.data.frame(Titanic),
       aes(axis1 = Class, axis2 = Sex, axis3 = Age,
           y= Freq)) +
  scale_x_discrete(limits = c("Class", "Sex", "Age"), expand = c(.1, .05)) +
  geom_alluvium(aes(fill = Survived)) +
  geom_stratum() + geom_text(stat = "stratum", label.strata = TRUE) +
  theme_minimal() +
  ggtitle("passengers on the maiden voyage of the Titanic",
          "stratified by demographics and survival")


参数说明:data设置数据源,axis设置显示的柱,weight为数值,geom_alluvium为冲击图组间面积连接并按生存率比填充分组,geom_stratum()每种有柱状图,geom_text()显示柱状图中标签,theme_minimal()主题样式的一种,ggtitle()设置图标题。

转换成 long格式 :to_lodes_form

titanic_long <- to_lodes_form(data.frame(Titanic),
                         key = "Demographic",
                         axes = 1:3)
> head(titanic_long)
  Survived Freq alluvium Demographic stratum
1       No    0        1       Class     1st
2       No    0        2       Class     2nd
3       No   35        3       Class     3rd
4       No    0        4       Class    Crew
5       No    0        5       Class     1st
6       No    0        6       Class     2nd
ggplot(data = titanic_long,
       aes(x = Demographic, stratum = stratum, alluvium = alluvium,
           y = Freq, label = stratum)) +
  geom_alluvium(aes(fill = Survived)) +
  geom_stratum() + geom_text(stat = "stratum") +
  theme_minimal() +
  ggtitle("passengers on the maiden voyage of the Titanic",
          "stratified by demographics and survival")

使用coord_flip函数进行X轴与Y轴的对调

ggplot(as.data.frame(Titanic),
       aes(y = Freq,
           axis1 = Survived, axis2 = Sex, axis3 = Class)) +
  geom_alluvium(aes(fill = Class),
                width = 0, knot.pos = 0, reverse = FALSE) +
  guides(fill = FALSE) +
  geom_stratum(width = 1/8, reverse = FALSE) +
  geom_text(stat = "stratum", label.strata = TRUE, reverse = FALSE) +
  scale_x_continuous(breaks = 1:3, labels = c("Survived", "Sex", "Class")) +
  coord_flip() +
  ggtitle("Titanic survival by class and sex")
非等高冲击图
data(Refugees, package = "alluvial")
country_regions <- c(
  Afghanistan = "Middle East",
  Burundi = "Central Africa",
  `Congo DRC` = "Central Africa",
  Iraq = "Middle East",
  Myanmar = "Southeast Asia",
  Palestine = "Middle East",
  Somalia = "Horn of Africa",
  Sudan = "Central Africa",
  Syria = "Middle East",
  Vietnam = "Southeast Asia"
)
Refugees$region <- country_regions[Refugees$country]
ggplot(data = Refugees,
       aes(x = year, y = refugees, alluvium = country)) +
  geom_alluvium(aes(fill = country, colour = country),
                alpha = .75, decreasing = FALSE) +
  scale_x_continuous(breaks = seq(2003, 2013, 2)) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = -30, hjust = 0)) +
  scale_fill_brewer(type = "qual", palette = "Set3") +
  scale_color_brewer(type = "qual", palette = "Set3") +
  facet_wrap(~ region, scales = "fixed") +
  ggtitle("refugee volume by country and region of origin")
Warning message:
In f(...) :
  Some differentiation aesthetics vary within alluvia, and will be diffused by their first value.
Consider using `geom_flow()` instead.
登高非等量关系
data(majors)
majors$curriculum <- as.factor(majors$curriculum)
ggplot(majors,
       aes(x = semester, stratum = curriculum, alluvium = student,
           fill = curriculum, label = curriculum)) +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  geom_flow(stat = "alluvium", lode.guidance = "rightleft",
            color = "darkgray") +
  geom_stratum() +
  theme(legend.position = "bottom") +
  ggtitle("student curricula across several semesters")
流状态随时间转换
data(vaccinations)
levels(vaccinations$response) <- rev(levels(vaccinations$response))
ggplot(vaccinations,
       aes(x = survey, stratum = response, alluvium = subject,
           y = freq,
           fill = response, label = response)) +
  scale_x_discrete(expand = c(.1, .1)) +
  geom_flow() +
  geom_stratum(alpha = .5) +
  geom_text(stat = "stratum", size = 3) +
  theme(legend.position = "none") +
  ggtitle("vaccination survey responses at three points in time")

2. ggforce: Visual Guide

data <- reshape2::melt(Titanic)
head(data)
  Class    Sex   Age Survived value
1   1st   Male Child       No     0
2   2nd   Male Child       No     0
3   3rd   Male Child       No    35
4  Crew   Male Child       No     0
5   1st Female Child       No     0
6   2nd Female Child       No     0
data <- gather_set_data(data, 1:4)
head(data)
  Class    Sex   Age Survived value id     x    y
1   1st   Male Child       No     0  1 Class  1st
2   2nd   Male Child       No     0  2 Class  2nd
3   3rd   Male Child       No    35  3 Class  3rd
4  Crew   Male Child       No     0  4 Class Crew
5   1st Female Child       No     0  5 Class  1st
6   2nd Female Child       No     0  6 Class  2nd
ggplot(data, aes(x, id = id, split = y, value = value)) +
  geom_parallel_sets(aes(fill = Sex), alpha = 0.3, axis.width = 0.1) +
  geom_parallel_sets_axes(axis.width = 0.1) +
  geom_parallel_sets_labels(colour = 'white')

巨人的肩膀

ggalluvial : Alluvial Diagrams in ggplot2
ggforce: Visual Guide
桑基图(Sankey)的简单实现
桑基图怎么看怎么画(附R代码)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 1240) 成品地址:https://public.tableau.com/profile/dannyer520#...
    黑喵爱吃棉花糖阅读 6,555评论 7 14
  • 桑基图是一种方便说明信息、资源流动和图形,图形中的分、合正如漂流的分叉与汇合,如可用于比较不同簇间关系,可以用来表...
    村狗儿阅读 3,684评论 1 8
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,849评论 6 13
  • 人是个群居动物,趋吉避凶,趋暖避冬。 我确实是一个怪物,反其道而行之。本也想好,过过就算了。 选择了在北京度过一个...
    清华个见阅读 449评论 0 3
  • 这周看了两部电影,性价比都很高,因为用了信用卡优惠,每张票才5块钱。但和它的优惠价格相比,影片质量只能说……一般般...
    Depressed猪阅读 618评论 0 50