0

I am trying to create a faceted alluvial plot with labels for the stratums on the first axis repelled to the left and left justified and the labels on the right repelled to the right and right justified.

# Small working example

# Install Packages and Libraries
install.packages("ggplot2")
install.packages("ggalluvial")
install.packages("ggrepel")
library(ggplot2)
library(ggalluvial)
library(ggrepel)

# Data Frame with 2 regions, 3 supply sectors and 3 demand sectors
df <- data.frame(region = c("A","A","A","B","B","B"),
                 supplySector = c("coal","gas","wind","coal","gas","wind"),
                 demandSector = c("resid","indus","ag","resid","indus","ag"),
                 value = 10*runif(6)); df    

# Faceted plot with ggrepel (nudge_x and hjust assigned for each label) works.
p <- ggplot(df, aes(y = value, axis1 = supplySector, axis2 = demandSector, group=region)) +
  ggalluvial::geom_alluvium(aes(fill = supplySector), width = 1/12, color="black", alpha=0.6) +
  ggalluvial::geom_stratum(width = 1/12, fill = "grey70", color = "grey10", alpha=1) +
  scale_x_discrete(limits = c("supplySector", "demandSector"), expand = c(0.3,0),drop=F) +
  facet_wrap(region~.) +
  ggrepel::geom_text_repel(stat = "stratum", label.strata = TRUE, direction = "y", 
                           size = 4, segment.color = 'grey50', 
                           nudge_x = rep(c(-3,-3,-3,3,3,3),2),
                           hjust = rep(c(1,1,1,-1,-1,-1),2)); p

Example plot p

# Faceted plot with ggrepel (nudge_x and hjust assigned for each label)
# does not work when different number of variables in each facet

df1 <- df[-nrow(df),]; df1  # Remove one of the rows from df

# So this gives the following plot with different alluvia in each facet
p1 <- ggplot(df1, aes(y = value, axis1 = supplySector, axis2 = demandSector, group=region)) +
  ggalluvial::geom_alluvium(aes(fill = supplySector), width = 1/12, color="black", alpha=0.6) +
  ggalluvial::geom_stratum(width = 1/12, fill = "grey70", color = "grey10", alpha=1) +
  scale_x_discrete(limits = c("supplySector", "demandSector"), expand = c(0.3,0),drop=F) +
  facet_wrap(region~.); p1

Example plot with variable alluvia: p1

# If we try and label these and assigns the nudge and hjust for each axis we get an error
# It expects the same length vector for nudge and hjust for each facet
p1 + ggrepel::geom_text_repel(stat = "stratum", label.strata = TRUE, direction = "y", 
                              size = 4, segment.color = 'grey50',
                              nudge_x = rep(c(-3,-3,-3,3,3,3),2),
                              hjust=rep(c(1,1,1,-1,-1,-1),2))
# Gives error: Error: Aesthetics must be either length 1 or the same as the data (10): hjust

# If we adjust the vectors for nudge_x and hjust to 10
p1 + ggrepel::geom_text_repel(stat = "stratum", label.strata = TRUE, direction = "y", 
                              size = 4, segment.color = 'grey50',
                              nudge_x = c(-3,-3,-3,3,3,3,-3-3,3,3),
                              hjust = c(1,1,1,-1,-1,-1,1,1,-1,-1))

# Get Error: Error in data.frame(x = data$x + nudge_x, y = data$y + nudge_y) :
#           arguments imply differing number of rows: 9, 6
#           In addition: Warning message:
#           In data$x + nudge_x :
#           longer object length is not a multiple of shorter object length

# It can be plotted without specifying the nudge_x and hjust values
p1 + ggrepel::geom_text_repel(stat = "stratum", label.strata = TRUE, direction = "y", 
                              size = 4, segment.color = 'grey50')

Example plot with variable alluvia labels no repel: p1Norepel

In summary, what I am trying to do is:

  • For plot p1 (with different number of alluvia in different facets)
  • Label each x axis stratum column
  • Have axis1 labels repel to the left and be left justified
  • Have axis2 labels repel to the right and be right justified

This answer suggested the different vector length for labels but it doesn't work for varying facets. Labelling and theme of ggalluvial plot in R

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
zarrar
  • 47
  • 7

1 Answers1

0

This is tricky! The nudge_* and *just arguments generally aren't dynamic. One way you could solve for this is to dig into the guts using ggplot_build()

ggplot_build() has all of the "instructions" of how ggplot() builds the chart. You can edit the data and then run plot(ggplot_gtable()) to see the plot with your modifications. I have added comments to help explain these steps.

# here is the base plot + the new layer for labels
plot_and_label <-
  p1 +
  geom_text_repel(
    stat = "stratum", label.strata = TRUE,
    direction = "y", size = 4,
    segment.color = 'grey50',
    nudge_x = 0
  )

# this is the plot under the hood
gg_guts <- ggplot_build(plot_and_label)

# the geom_text_repel layer was the 3rd one we added so you can 
# access and edit it like this
gg_guts$data[[3]] <-   
  gg_guts$data[[3]] %>%
  mutate(hjust = ifelse(x%%2 == 1, 2, -2))

# once you've made your adjustments, you can plot it again
plot(ggplot_gtable(gg_guts))

enter image description here

yake84
  • 3,004
  • 2
  • 19
  • 35
  • Thanks @yake84. This works great and answers my question! So just as a follow on, I don't understand what kind of an object "plot(ggplot_gtable(gg_guts))" is. In my full code I add on other items to this plot which edit the axes etc. How would I, for example, continue to add a theme to the plot after doing what you have suggested. So something like: plot(ggplot_gtable(gg_guts)) + theme_bw() doesn't work. – zarrar May 15 '19 at 18:05
  • I don't know if it is possible. The `ggplot_gtable()` turns it into a big table of grobs. I would do all the formatting before the `ggplot_build()`. If you want more info/ideas, see this thread https://stackoverflow.com/a/46161273/4650934 – yake84 May 15 '19 at 20:03