Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

r - Annotate plot with text left to y axis title

Hello everyone and happy new year !!! I would need help in order to improve a ggplot figure.

 df1 <- structure(list(x = structure(c(1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 
    1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L), .Label = c("a", "b", "c", "d"
    ), class = "factor"), y = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 
    3L, 3L, 3L, 4L, 4L, 4L, 4L), z = c(-0.130312994048691, 0.714073455094197, 
    -0.156691533710652, 0.39894708481517, 0.644656691110372, -1.18694632145378, 
    -0.257204564112021, 1.34927378214664, -1.03584454605617, 0.148408762003154, 
    0.501192202628166, 0.511688097742773, -0.947953281835912, 0.0861048893885463, 
    1.55144239199118, 0.20220333664676)), class = "data.frame", row.names = c(NA, 
    -16L))

library(ggplot2)
    df1$facet <- ifelse(df1$x %in% c("c", "d"), "cd", df1$x)
    
    p1 <- ggplot(df1, aes(x = x, y = y))
    p1 <- p1 + geom_tile(aes(fill = z), colour = "grey20")
    p1 <- p1 + scale_fill_gradient2(
      low = "darkgreen",
      mid = "white",
      high = "darkred",
      breaks = c(min(df1$z), max(df1$z)),
      labels = c("Low", "High")
    )
    p1 + facet_grid(.~facet, space = "free", scales = "free_x") +
      theme(strip.text.x = element_blank())

With this code (inspired from here) I get this figure:

enter image description here

But I wondered if someone had an idea in order to :

  • To add sub Y axis element (here noted as Element 1-3) where Element1 (first box); Element2 (2 and 3 box) and Element3 (4 box)

the result should be something like:

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is not easy! As usual with plot annotation, there are basically three main options.

  • annotate outside the plot area with clipping turned off.
  • create plots and paste them together
  • mess with the grobs.

In my plots, I've decided to replace your scale_fill call with something simpler.

Here option 1:

library(tidyverse)
                                                                                                                                                        
df1$facet <- fct_collapse(df1$x, cd = c("c", "d")) # slightly changed

# Create data frames for segments and labels
yseg <- c(.5, 1.5, 3.5, 4.5) 
df_txt <- data.frame(x = -0.75, y = c(1, 2.5, 4), 
                     label = paste0("element", 1:3), facet = "a")
df_seg <- data.frame(x = 0.5, xend = -1.5, y = yseg, facet = "a")

ggplot(df1, aes(x = x, y = y)) + 
  geom_tile(aes(fill = z), colour = "grey20") + 
  scale_fill_distiller(palette = "RdBu") + 
  scale_x_discrete(expand = c(0, 0)) + # kind of necessary
  facet_grid(.~facet, scales = "free_x", space = "free") +
  geom_segment(data = df_seg, 
               aes(x = x, xend = xend, y = yseg,yend = yseg), lty = "dashed",
               color = "black") +
  geom_text(data = df_txt, aes(x = x, y = y, label = label), hjust = 0.5) +
  coord_cartesian(xlim = c(0.5, NA), clip = "off") + # this is how you turn clipping off
  theme(strip.text.x = element_blank(),
        plot.margin = margin(l = 2, unit = "inch"))

Another option is to make 3 different plots - arguably less hacky. Well. I still think it's hacky.

library(patchwork)
# create three plots. You could obviously also create four plots. 

p_right <- 
  ggplot(filter(df1, x != "a"), aes(x = x, y = y)) + 
  geom_tile(aes(fill = z), colour = "grey20") + 
  scale_x_discrete(expand = c(0, 0)) +
  scale_fill_distiller(palette = "RdBu", limits = c(-1, 1.5)) + # You need to set the same scale limits
  facet_grid(.~facet, scales = "free_x", space = "free") +
  theme(axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        plot.margin = margin(),
        panel.spacing = unit(0.5, "lines")
        )
  
p_left <-
  ggplot(filter(df1, x == "a"), aes(x = x, y = y)) + 
  geom_tile(aes(fill = z), colour = "grey20") + 
  scale_x_discrete(expand = c(0, 0)) + 
  scale_fill_distiller(palette = "RdBu", limits = c(-1, 1.5)) + # You need to set the same scale limits
  facet_grid(.~facet, scales = "free_x", space = "free") +
  theme(plot.margin = margin(r = 0.4, unit = "lines"),
        axis.title.y = element_text(margin = margin()))

p_seg <- 
ggplot() +
  geom_segment(data = df_seg, 
    aes(x = x, xend = xend, y = yseg,yend = yseg), lty = "dashed",
    color = "black") +
  scale_x_discrete(expand = c(0, 0)) +
  geom_text(data = df_txt, aes(x = x, y = y, label = label), hjust = 0.5) +
  theme_void() # important short cut to get rid of "everything but... "

p_seg + p_left + p_right + 
  plot_layout(nrow = 1, guides = "collect", 
              widths = c(2, 1, 3)) &
# the theme call in patchwork sets theme options globally, for all plots. 
  theme(strip.text.x = element_blank(),
        axis.title.x = element_blank())

There is now a gap between segments and plots, so not quite the expected result, but visually fairly close. Also it requires (too much?) trial and error with the right margins / relative plot widths to get everything to look nice... On the other hand, this option probably gives you the biggest flexibility and you don't need to worry too much of the effects of scale expansion. So I personally would prefer the option. Would probably create four separate plots for full control of the gaps.

The last option would be to mess with the grobs, which I admit I am not very good at, so I will leave it with those two options. Hope this helped.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...