Reputation: 2187
I have tried to create a Sankey Diagram using both the ggalluvial and networkd3 packages and failed to date. Ideally I would like to understand how to get what I want to do in both.
Data is generated as follows:
dat <- data.frame(customer = c(rep(c(1, 2), each=3), 3, 3),
holiday_loc = c("SA", "SA", "AB", "SA", "SA", "SA", "AB", "AB"),
holiday_num = c(1, 2, 3, 1, 2, 3, 1, 2))
dat_wide <- dat %>%
spread(key=holiday_num, value=holiday_loc`)
Not sure whether dat or dat_wide is more appropriate? I want the output to visualise the following information (where the number in brackets is the frequency and therefore width of the flow)
SA -(2) - SA - (1) - AB
- (1) - SA
AB -(1) - AB
I followed the instructions on this link for networkd3 Sankey diagram for Discrete State Sequences in R using networkd3, however I ended up with loops in the diagram.
A similar diagram of what I want is shown in the below image: [![Sankey Diagram taken from SAS VA][2]][2]
Suggestions and help will be greatly appreciated...
Thanks!
[2]: https://i.sstatic.net/wTJ1k.png
Upvotes: 4
Views: 2241
Reputation: 8848
The core problem with your data (in networkD3
terms) is that you have nodes with the same name, so you need to distinguish them, at least while you're processing the data.
Combine the location and the number information to make distinguishable nodes, then transform your data into a links data frame, like this...
links <-
dat %>%
mutate("source" = paste(holiday_loc, holiday_num, sep = "_")) %>%
group_by(customer) %>%
arrange(holiday_num) %>%
mutate("target" = lead(source)) %>%
ungroup() %>%
arrange(customer) %>%
filter(!is.na(target)) %>%
select(source, target)
From that, you can build a nodes data frame which contains one row for each distinct node, like this...
node_names <- factor(sort(unique(c(as.character(links$source),
as.character(links$target)))))
nodes <- data.frame(name = node_names)
Then convert the links data frame to use the index (0-indexed because it ultimately gets passed to JavaScript) of the node in the nodes data frame, like this...
links <- data.frame(source = match(links$source, node_names) - 1,
target = match(links$target, node_names) - 1,
value = 1)
At this point, if you want the nodes to have non-distinct names, you can change that now, like this...
nodes$name <- sub("_[0-9]$", "", nodes$name)
And now you can plot it...
library(networkD3)
sankeyNetwork(links, nodes, "source", "target", "value", "name")
Upvotes: 5
Reputation: 185
I find the alluvial package useful for that task, but I don't know if that is what your locking for:
library(tidyverse)
library(alluvial)
dat <- data.frame(customer = c(rep(c(1, 2), each=3), 3, 3),
holiday_loc = c("SA", "SA", "AB", "SA", "SA", "SA", "AB", "AB"),
holiday_num = c(1, 2, 3, 1, 2, 3, 1, 2))
dat_summarized <- dat %>% group_by(holiday_num, holiday_loc, customer) %>%
summarise(n = n()) %>% mutate(color = recode(customer,
`1` = "cadetblue1",
`2` = "cadetblue2",
`3` = "cadetblue3"))
alluvial(dat_summarized[1:3],
freq = dat_summarized$n,
col = dat_summarized$color)
Upvotes: 0