corybrunson / ggalluvial

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

Add labels on the right of alluvial plot #114

Closed willizhang closed 1 year ago

willizhang commented 1 year ago

Hi, I would like to ask if it would be possible to add labels on the right of the alluvial plot, as below:

image

https://r-graph-gallery.com/web-line-chart-with-labels-at-end-of-line.html

I tried to use geom_text_repel(), but probably the code I wrote was wrong.

library(ggalluvial) titanic_wide <- data.frame(Titanic)

ggplot( data = titanic_wide, aes(axis1 = Class, axis2 = Sex, axis3 = Age, y = Freq) ) + scale_x_discrete(limits = c("Class", "Sex", "Age"), expand = c(.2, .05)) + xlab("Demographic") + geom_alluvium(aes(fill=Class)) + geom_stratum(aes(fill=Class)) + geom_text(stat = "stratum", aes(label = after_stat(stratum)) ) + theme_minimal() + ggtitle( "passengers on the maiden voyage of the Titanic", "stratified by demographics and survival" )+ scale_fill_manual( values=c("red","orange","green","blue"), breaks=c("1st","2nd","3rd","Crew"), labels=c("1st","2nd","3rd","Crew"), na.value = NA ) + geom_text_repel( aes(color = Class, label = Freq) )

corybrunson commented 1 year ago

Hi @zhangguoqianggu, thanks for noting this. I should probably explicitly state on the labeling vignette that the data must be in lodes form for either solution to work. That requires making some changes to the structure of the ggplot() layers and aesthetics, but also it requires familiarity with another trick: creating a new variable for the labels that only takes values on the rightmost axis. So this can't be done automatically at present; the user must tell the plot which alluvia or strata get labels. Maybe this behavior could be made into a convenient feature, but i haven't had time to develop the package much in the last year and might not for another still.

Here is a plot based on your code above. Maybe it's not exactly what you want, but it should get you started.

library(ggalluvial)
#> Loading required package: ggplot2
library(ggrepel)
titanic_wide <- data.frame(Titanic)
titanic_long <- to_lodes_form(titanic_wide, axes = 1:4,
                              key = "variable", value = "value", id = "cohort",
                              diffuse = Class)

ggplot(
  data = titanic_long,
  aes(x = variable, stratum = value, alluvium = cohort, y = Freq)
) +
  geom_alluvium(aes(fill=Class)) +
  geom_stratum(aes(fill=Class)) +
  geom_text(stat = "stratum", aes(label = after_stat(stratum)) ) +
  theme_minimal() +
  ggtitle(
    "passengers on the maiden voyage of the Titanic",
    "stratified by demographics and survival"
  )+
  scale_fill_manual(
    values=c("red","orange","green","blue"),
    breaks=c("1st","2nd","3rd","Crew"),
    labels=c("1st","2nd","3rd","Crew"),
    na.value = NA,
    aesthetics = c("fill", "color")
  ) +
  geom_text_repel(stat = "alluvium",
    aes(color = Class,
        label = ifelse(variable == "Survived" & Freq >= 10, Freq, NA)),
    direction = "y", nudge_x = .5)
#> Warning: Removed 108 rows containing missing values (`geom_text_repel()`).

Created on 2023-06-01 with reprex v2.0.2

willizhang commented 1 year ago

Thank you so much for your help! This looks cool! 😄

corybrunson commented 1 year ago

Great, thanks for confirming!

Yingjie4Science commented 5 months ago

Hi @corybrunson I have a similar but slightly different question: in your last example here, is it possible to only show the labels on the last axis, i.e., "ms460_NSA".

I have tried label = ifelse(survey == "ms460_NSA" & after_stat(n)>10, after_stat(n), NA)), but with an error "object 'survey' not found"