corybrunson / ggalluvial

ggplot2 extension for alluvial plots
http://corybrunson.github.io/ggalluvial/
GNU General Public License v3.0
497 stars 34 forks source link

[Question] Is there a way to avoid interlaving alluvia in the same stratum? #77

Closed Generalized closed 3 years ago

Generalized commented 3 years ago

On the picture below, in the black ellipse, the red, green and cyan bands don't form continuous alluvia, but rather they interlave with each other.

obraz

It, however, goes to the same strata (from Drug A at Visit 1 to Drug A at Visit 2). Is it possible to display the alluvia ordered by colors?

I mean - all red, then all cyan, then all green with no "holes", no "crossing" as long as it belongs to the same strata?

obraz

Yes, I know not the PatiendID is tracked (by an alluvium), but is it possible to change the sorting of the bars to purposefully skip the individual PatientId alluvia and show how many - in general - patients switched from Drug A to drug B, not necessarily in the same order at each time point?

d <- structure(list(PatientId = c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 
2L, 2L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 
5L, 6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 8L, 
9L, 9L, 9L, 9L, 9L, 10L, 10L, 10L, 10L, 10L, 11L, 11L, 11L, 11L, 
11L, 12L, 12L, 12L, 12L, 12L, 13L, 13L, 13L, 13L, 13L, 14L, 14L, 
14L, 14L, 14L, 15L, 15L, 15L, 15L, 15L, 16L, 16L, 16L, 16L, 16L, 
17L, 17L, 17L, 17L, 17L, 18L, 18L, 18L, 18L, 18L, 19L, 19L, 19L, 
19L, 19L, 20L, 20L, 20L, 20L, 20L, 21L, 21L, 21L, 21L, 21L, 22L, 
22L, 22L, 22L, 22L, 23L, 23L, 23L, 23L, 23L, 24L, 24L, 24L, 24L, 
24L, 25L, 25L, 25L, 25L, 25L, 26L, 26L, 26L, 26L, 26L, 27L, 27L, 
27L, 27L, 27L, 28L, 28L, 28L, 28L, 28L, 29L, 29L, 29L, 29L, 29L, 
30L, 30L, 30L, 30L, 30L), Time = c(Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4", Baseline = "Baseline", `Visit 1` = "Visit 1", 
`Visit 2` = "Visit 2", `Visit 3` = "Visit 3", `Visit 4` = "Visit 4", 
Baseline = "Baseline", `Visit 1` = "Visit 1", `Visit 2` = "Visit 2", 
`Visit 3` = "Visit 3", `Visit 4` = "Visit 4", Baseline = "Baseline", 
`Visit 1` = "Visit 1", `Visit 2` = "Visit 2", `Visit 3` = "Visit 3", 
`Visit 4` = "Visit 4"), Drug = structure(c(1L, 1L, 1L, 1L, 1L, 
3L, 1L, 2L, 2L, 2L, 2L, 1L, 2L, 1L, 2L, 3L, 3L, 1L, 1L, NA, 1L, 
1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 2L, 1L, 1L, 1L, 1L, 4L, 2L, 
1L, 3L, 3L, 3L, NA, 3L, NA, 3L, 1L, 1L, 3L, 2L, 1L, 3L, 1L, 3L, 
1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, 1L, 1L, 
1L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 3L, 1L, 3L, 1L, 2L, 
1L, 1L, 1L, 1L, 1L, NA, NA, NA, 4L, 1L, 1L, 1L, 1L, 4L, 1L, 3L, 
1L, 2L, 2L, 2L, 2L, 1L, 2L, 1L, 2L, 3L, 3L, 1L, 1L, 2L, 4L, 4L, 
1L, 4L, 4L, 4L, 1L, 1L, 4L, 4L, 3L, 3L, NA, NA, NA, 3L, 1L, 2L, 
NA, NA, 2L, 1L, 2L, 1L, 2L, 3L, 3L, NA, 1L, NA, 4L, 2L, 1L, NA, 
NA), .Label = c("Drug A", "Drug B", "Drug C", "Drug D"), class = "factor"), 
    Group = c("X", "X", "X", "X", "X", "X", "X", "X", "X", "X", 
    "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", 
    "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", 
    "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", 
    "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", 
    "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", 
    "X", "X", "X", "X", "X", "Y", "Y", "Y", "Y", "Y", "Y", "Y", 
    "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", 
    "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", 
    "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", 
    "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", 
    "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", 
    "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y"), DrugNew = structure(c(1L, 
    1L, 1L, 1L, NA, 1L, 2L, 2L, 2L, NA, 1L, 2L, 1L, 2L, NA, 3L, 
    1L, 1L, NA, NA, 1L, 1L, 1L, 1L, NA, 2L, 2L, 2L, 1L, NA, 1L, 
    1L, 1L, 1L, NA, 2L, 1L, 3L, 3L, NA, NA, 3L, NA, 3L, NA, 1L, 
    3L, 2L, 1L, NA, 1L, 3L, 1L, 2L, NA, 1L, 1L, 1L, 1L, NA, 1L, 
    1L, 1L, 1L, NA, 1L, 1L, 1L, 1L, NA, 4L, 4L, 4L, 4L, NA, 4L, 
    4L, 4L, 4L, NA, 1L, 3L, 1L, 2L, NA, 1L, 1L, 1L, 1L, NA, NA, 
    NA, 4L, 1L, NA, 1L, 1L, 4L, 1L, NA, 1L, 2L, 2L, 2L, NA, 1L, 
    2L, 1L, 2L, NA, 3L, 1L, 1L, 2L, NA, 4L, 1L, 4L, 4L, NA, 1L, 
    1L, 4L, 4L, NA, 3L, NA, NA, NA, NA, 1L, 2L, NA, NA, NA, 1L, 
    2L, 1L, 2L, NA, 3L, NA, 1L, NA, NA, 2L, 1L, NA, NA, NA), .Label = c("Drug A", 
    "Drug B", "Drug C", "Drug D"), class = "factor"), DrugOld = structure(c(NA, 
    1L, 1L, 1L, 1L, NA, 3L, 1L, 2L, 2L, NA, 2L, 1L, 2L, 1L, NA, 
    3L, 3L, 1L, 1L, NA, 1L, 1L, 1L, 1L, NA, 2L, 2L, 2L, 2L, NA, 
    2L, 1L, 1L, 1L, NA, 4L, 2L, 1L, 3L, NA, 3L, NA, 3L, NA, NA, 
    1L, 1L, 3L, 2L, NA, 3L, 1L, 3L, 1L, NA, 1L, 1L, 1L, 1L, NA, 
    1L, 1L, 1L, 1L, NA, NA, 1L, 1L, 1L, NA, 4L, 4L, 4L, 4L, NA, 
    4L, 4L, 4L, 4L, NA, 3L, 1L, 3L, 1L, NA, 1L, 1L, 1L, 1L, NA, 
    NA, NA, NA, 4L, NA, 1L, 1L, 1L, 4L, NA, 3L, 1L, 2L, 2L, NA, 
    2L, 1L, 2L, 1L, NA, 3L, 3L, 1L, 1L, NA, 4L, 4L, 1L, 4L, NA, 
    4L, 1L, 1L, 4L, NA, 3L, 3L, NA, NA, NA, 3L, 1L, 2L, NA, NA, 
    2L, 1L, 2L, 1L, NA, 3L, 3L, NA, 1L, NA, 4L, 2L, 1L, NA), .Label = c("Drug A", 
    "Drug B", "Drug C", "Drug D"), class = "factor")), row.names = c(NA, 
-150L), class = c("tbl_df", "tbl", "data.frame"))
> head(d)
  PatientId Time     Drug   Group DrugNew DrugOld
      <int> <chr>    <fct>  <chr> <fct>   <fct>  
1         1 Baseline Drug A X     Drug A  NA     
2         1 Visit 1  Drug A X     Drug A  Drug A 
3         1 Visit 2  Drug A X     Drug A  Drug A 
4         1 Visit 3  Drug A X     Drug A  Drug A 
5         1 Visit 4  Drug A X     NA      Drug A 
6         2 Baseline Drug C X     Drug A  NA     
h_just <- 7.5

ggplot(data = d,
       aes(x = Time, stratum=Drug, alluvium = PatientId)) +
    geom_flow(aes(fill = Drug), 
              stat = "alluvium", 
              lode.guidance = "frontback", 
              cement.alluvia = FALSE, 
              width = .5, 
              color="darkgrey") +
    geom_stratum(aes(fill = Drug, color=Drug), alpha = .8, width = .5) + 
    theme_bw() +
    theme(legend.position="top") +
    labs(title = "Change in treatment over time") +
    theme(plot.title = element_text(hjust = 0.5)) +
    geom_text(aes(label=after_stat(sprintf("%d (%.1f%%)", n, 100*prop))),
              stat = "stratum", 
              size=3.1) +
    geom_text(aes(col = DrugOld, 
                  label=after_stat(ifelse(flow ==   "to", n, "")),  
                  hjust = after_stat((flow ==   "to") *  h_just+1)), 
              stat = "flow", 
              size=3, 
              show.legend = FALSE) +
    geom_text(aes(col = DrugNew, 
                  label=after_stat(ifelse(flow == "from", n, "")), 
                  hjust = after_stat((flow == "from") * -1*h_just)), 
              stat = "flow", 
              size=3, 
              show.legend = FALSE) +
    ylab("Count")

obraz

corybrunson commented 3 years ago

Hi @Generalized, and thanks for checking. What you're describing sounds most like what the flow stat was designed for. While you're using the flow geom, it's paired with the alluvium stat (stat = "alluvium"). Try switching this to stat = "flow" and see if the result is what you want. If not, then you might try using the aes.bind parameter of stat_alluvium(). Failing that, could you describe how what you want differs from what these changes achieve?

Generalized commented 3 years ago

Oh my! It was so trivial and I couldn't figure it out! It works like a charm. Thank you!

obraz