Phi*_*psy 5 plot r sankey-diagram networkd3 htmlwidgets
目前,我正在尝试networkD3按照Chris Grandrud(https://christophergandrud.github.io/networkD3/)的说明创建一个包含交互式的Sankey .
我不明白的是表格式,因为他只使用两列来可视化更多的过渡.更具体地说,我有一个包含四列代表4年的数据集.这些列中有不同的酒店名称,而每行代表一个客户,这四年内被"跟踪".
URL <- paste0(
"https://cdn.rawgit.com/christophergandrud/networkD3/",
"master/JSONdata/energy.json")
Energy <- jsonlite::fromJSON(URL)
sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
units = "TWh", fontSize = 12, nodeWidth = 30)
Run Code Online (Sandbox Code Playgroud)
为了向您概述我的数据,请点击此处截图:

我会给你更多"编码"信息,但由于我对RI这个话题很新,希望你能在这个问题上跟随我的思路.如果没有,请不要犹豫不决.
谢谢 :)
小智 11
您需要两个数据框:一个列出所有节点(包含名称),另一个列出链接.后者包含三列,源节点,目标节点和一些值,表示链接的强度或宽度.在链接数据框中,您通过节点数据帧中的(从零开始)位置来引用节点.
假设您的数据如下:
df <- data.frame(Year1=sample(paste0("Hotel", 1:4), 1000, replace = TRUE),
Year2=sample(paste0("Hotel", 1:4), 1000, replace = TRUE),
Year3=sample(paste0("Hotel", 1:4), 1000, replace = TRUE),
Year4=sample(paste0("Hotel", 1:4), 1000, replace = TRUE),
stringsAsFactors = FALSE)
Run Code Online (Sandbox Code Playgroud)
对于图表,您不仅需要区分酒店,还要区分酒店/年份组合,因为每个酒店都应该是一个节点:
df$Year1 <- paste0("Year1_", df$Year1)
df$Year2 <- paste0("Year2_", df$Year2)
df$Year3 <- paste0("Year3_", df$Year3)
df$Year4 <- paste0("Year4_", df$Year4)
Run Code Online (Sandbox Code Playgroud)
链接是酒店之间从一年到下一年的"过渡":
library(dplyr)
trans1_2 <- df %>% group_by(Year1, Year2) %>% summarise(sum=n())
trans2_3 <- df %>% group_by(Year2, Year3) %>% summarise(sum=n())
trans3_4 <- df %>% group_by(Year3, Year4) %>% summarise(sum=n())
colnames(trans1_2)[1:2] <- colnames(trans2_3)[1:2] <- colnames(trans3_4)[1:2] <- c("source","target")
links <- rbind(as.data.frame(trans1_2),
as.data.frame(trans2_3),
as.data.frame(trans3_4))
Run Code Online (Sandbox Code Playgroud)
最后,数据帧需要相互引用:
nodes <- data.frame(name=unique(c(links$source, links$target)))
links$source <- match(links$source, nodes$name) - 1
links$target <- match(links$target, nodes$name) - 1
Run Code Online (Sandbox Code Playgroud)
然后可以绘制图表:
library(networkD3)
sankeyNetwork(Links = links, Nodes = nodes, Source = "source",
Target = "target", Value = "sum", NodeID = "name",
fontSize = 12, nodeWidth = 30)
Run Code Online (Sandbox Code Playgroud)
可能有更优雅的解决方案,但这可能是您问题的起点.如果您不喜欢节点名称中的"Year ...",请在设置数据帧后删除它们.
这个问题经常出现......如何转换在多列的每一行上定义了多个链接/边的数据集。这是我如何将其转换为sankeyNetwork(以及许多其他处理边/链接/网络数据的包)使用的数据集类型......每行一个边/链接的数据集。
从示例数据集开始...
df <- read.csv(header = TRUE, as.is = TRUE, text = '
name,year1,year2,year3,year4
Bob,Hilton,Sheraton,Westin,Hyatt
John,Four Seasons,Ritz-Carlton,Westin,Sheraton
Tom,Ritz-Carlton,Westin,Sheraton,Hyatt
Mary,Westin,Sheraton,Four Seasons,Ritz-Carlton
Sue,Hyatt,Ritz-Carlton,Hilton,Sheraton
Barb,Hilton,Sheraton,Ritz-Carlton,Four Seasons
')
# name year1 year2 year3 year4
# 1 Bob Hilton Sheraton Westin Hyatt
# 2 John Four Seasons Ritz-Carlton Westin Sheraton
# 3 Tom Ritz-Carlton Westin Sheraton Hyatt
# 4 Mary Westin Sheraton Four Seasons Ritz-Carlton
# 5 Sue Hyatt Ritz-Carlton Hilton Sheraton
# 6 Barb Hilton Sheraton Ritz-Carlton Four Seasons
Run Code Online (Sandbox Code Playgroud)
tidyr的gather()函数将数据集转换为长格式NA“目标”的行(原始数据集最后一列中的节点将没有“目标”,因此这些行不指定链接)library(dplyr)
library(tidyr)
links <-
df %>%
mutate(row = row_number()) %>%
gather('column', 'source', -row) %>%
mutate(column = match(column, names(df))) %>%
group_by(row) %>%
arrange(column) %>%
mutate(target = lead(source)) %>%
ungroup() %>%
filter(!is.na(target))
# # A tibble: 24 x 4
# row column source target
# <int> <int> <chr> <chr>
# 1 1 1 Bob Hilton
# 2 2 1 John Four Seasons
# 3 3 1 Tom Ritz-Carlton
# 4 4 1 Mary Westin
# 5 5 1 Sue Hyatt
# 6 6 1 Barb Hilton
# 7 1 2 Hilton Sheraton
# 8 2 2 Four Seasons Ritz-Carlton
# 9 3 2 Ritz-Carlton Westin
# 10 4 2 Westin Sheraton
# # ... with 14 more rows
Run Code Online (Sandbox Code Playgroud)
现在数据已经是由“源”和“目标”列定义的每行一个链接的典型网络数据格式,并且可以与sankeyNetwork(). 但是,您可能希望引用同一事物的节点在您的情节中多次出现……如果有人在第 1 年访问了希尔顿,然后在第 3 年再次访问了希尔顿,您可能需要 2 个独立的节点,均命名为希尔顿,但出现在情节的不同部分。为了做到这一点,您必须在“源”和“目标”列中标识每个节点以及它们被访问的年份。这就是保持“行”和“列”变量会派上用场的地方。
将列索引附加到“源”名称,并将列索引 + 1 附加到“目标”名称,现在您将能够区分,例如,在第 1 年访问的 Hilton 节点和在第 3 年访问过的 Hilton 节点
links <-
links %>%
mutate(source = paste0(source, '_', column)) %>%
mutate(target = paste0(target, '_', column + 1)) %>%
select(source, target)
# # A tibble: 24 x 2
# source target
# <chr> <chr>
# 1 Bob_1 Hilton_2
# 2 John_1 Four Seasons_2
# 3 Tom_1 Ritz-Carlton_2
# 4 Mary_1 Westin_2
# 5 Sue_1 Hyatt_2
# 6 Barb_1 Hilton_2
# 7 Hilton_2 Sheraton_3
# 8 Four Seasons_2 Ritz-Carlton_3
# 9 Ritz-Carlton_2 Westin_3
# 10 Westin_2 Sheraton_3
# # ... with 14 more rows
Run Code Online (Sandbox Code Playgroud)
现在,您可以按照相当标准的过程使用源-目标链接列表来构建sankeyNetwork(). nodes使用在“源”和“目标”向量中找到的所有唯一节点创建一个数据框。将links数据框中的“源”和“目标”向量转换为数据框中节点的基于 0 的索引nodes。为links数据框中的每个链接添加一个任意值,因为sankeyNetwork(). 现在您可以从nodes数据框中的节点名称中删除附加的列索引,因为它们将仅用于标记结果图中的节点(因此它们是否唯一不再重要)。然后用sankeyNetwork()!
nodes <- data.frame(name = unique(c(links$source, links$target)))
links$source <- match(links$source, nodes$name) - 1
links$target <- match(links$target, nodes$name) - 1
links$value <- 1
nodes$name <- sub('_[0-9]+$', '', nodes$name)
library(networkD3)
library(htmlwidgets)
sankeyNetwork(Links = links, Nodes = nodes, Source = 'source',
Target = 'target', Value = 'value', NodeID = 'name')
Run Code Online (Sandbox Code Playgroud)