rggplot2ggalluvial

Alluvial diagram in R: Create an plot where the categories are aligned on the y-axis across time


How can I create an Alluvial-like diagram where the categories are hierarchically aligned on the y-axis (rather than fixed together) so that it does not look like individuals are going "downward" when going from medium to medium in the example plot below? And, where we still keep the representation of the number of cases (i.e., the thickness of the lines) and the wave-like forms.

#Example code

df_test <- data.frame(
  ID = 1:11,
  T1 = c("Low", "Very high", "Medium", "Low", "High", "Medium", "High", "Medium", "Low", "High", "Low"),
  T2 = c("High", "High", "Medium", "Very high", "High", "High", "Medium", "High", "Medium", "High", "Low")
)

# Set the desired order of categories for T1 and T7 to reflect the hierarchy
df_test$T1 <- factor(df_test$T1, levels = c("Very high","High",  "Medium", "Low"))
df_test$T2 <- factor(df_test$T2, levels = c("Very high","High",  "Medium", "Low"))

# Create an alluvial plot
ggplot(df_test, aes(axis1 = T1, axis2 = T2)) +
  geom_alluvium(aes(fill = T1), width = 1/12) +
  geom_stratum(width = 1/5) +
  geom_text(stat = "stratum", aes(label = after_stat(stratum)), size = 3) +
  scale_x_discrete(limits = c("T1", "T2")) +
  scale_fill_manual(values = c("Low" = "skyblue",
                               "Medium" = "orange",
                               "High" = "red",
                               "Very high" = "darkred")) +
  labs(title = "Transition of Categories from T1 to T2",
       x = "Time Points",
       y = "Number of individuals",
       fill = "Status") +
  theme_minimal() +
  theme(axis.text.x = element_text(face = "bold", size = 14),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        panel.grid.major.y = element_blank())

enter image description here

(im open for using other R-pacakges)


Solution

  • Geometrically, since the the strata of an alluvial plot's heights must represent weights, it is not always possible to arrange the alluvia so that they travel up if the category increases, travel down when the category decreases and go straight across if the category is unchanged. One alternative is to plot as a weighted graph instead, which preserves position, direction and flow size:

    library(igraph)
    library(tidyverse)
    library(tidygraph)
    library(ggraph)
    
    df_test %>%
      select(-ID) %>%
      mutate(T1 = paste("T1", T1), T2 = paste("T2", T2)) %>%
      graph_from_data_frame() %>%
      as_adj() %>%
      graph_from_adjacency_matrix(weighted = TRUE) %>%
      as_tbl_graph() %>%
      mutate(label = sub("T\\d ", "", name),
             xpos = 2 * as.numeric(factor(gsub("(T\\d).*", "\\1", name)))) %>%
      mutate(ypos = match(label, c("Low", "Medium", "High", "Very high"))) %>%
      ggraph(layout = "manual", x = xpos, y = ypos) +
      geom_edge_diagonal(aes(width = weight, color = factor(from)), flipped = T,
                         alpha = 0.5) +
      geom_node_circle(aes(r = 0.2), fill = "white") +
      geom_node_text(aes(label = label))  +
      scale_edge_width(range = c(8, 16), guide = "none") +
      scale_x_continuous(breaks = c(2, 4), labels = c("T1", "T2")) +
      guides(edge_colour = "none") +
      coord_equal() +
      theme_minimal() +
      theme(axis.line.x = element_line(), 
            axis.text.x = element_text(size = 20),
            panel.grid = element_blank(),
            axis.title = element_blank(),
            axis.text.y = element_blank())
    

    enter image description here