Search code examples
rggplot2labelfacet-grid

Retrieve facet labels from a ggplot or a gtable/gTree/grob/gDesc object


I have data I'm plotting using ggplot's facet_grid:

My data:

species <- c("spcies1","species2")
conditions <- c("cond1","cond2","cond3")
batches <- 1:6

df <- expand.grid(species=species,condition=conditions,batch=batches)

set.seed(1)
df$y <- rnorm(nrow(df))
df$replicate <- 1
df$col.fill <- paste(df$species,df$condition,df$batch,sep=".")

My plot:

integerBreaks <- function(n = 5, ...)
{
  library(scales)
  breaker <- pretty_breaks(n, ...)
  function(x){
    breaks <- breaker(x)
    breaks[breaks == floor(breaks)]
  }
}

library(ggplot2)
p <- ggplot(df,aes(x=replicate,y=y,color=col.fill))+
  geom_point(size=3)+facet_grid(~col.fill,scales="free_x")+
  scale_x_continuous(breaks=integerBreaks())+
  theme_minimal()+theme(legend.position="none",axis.title=element_text(size=8))

which gives: enter image description here

Obviously the labels are long and come out pretty messed up in the figure so I was wondering if there's a way edit these labels in the ggplot object (p) or the gtable/gTree/grob/gDesc object (ggplotGrob(p)).

I am aware that one way of getting better labels is to use the labeller function when the ggplot object is created but in my case I'm specifically looking for a way to edit the facet labels after the ggplot object has been created.


Solution

  • As I mentioned in the comments, the facet names are nested quite deeply within the gtable that ggplotGrob() gives you. However, this is still possible and since the OP explicitly wants to edit them after being plotted, you can do this with:

    library(grid)
    gg <- ggplotGrob(p)
    
    edited_grobs <- mapply(FUN = function(x, y) {
                                      x[["grobs"]][[1]][["children"]][[2]][["children"]][[1]][["label"]] <- y
                                      return(x)
                                  },
                            gg$grobs[which(grepl("strip-t",gg$layout$name))],
                            unique(gsub("cond","c", df$condition)),
                            SIMPLIFY = FALSE)
    
    gg$grobs[which(grepl("strip-t",gg$layout$name))] <- edited_grobs
    grid.draw(gg)
    

    enter image description here

    Note that this extracts all the strips using gg$grobs[which(grepl("strip-t",gg$layout$name))] and passes them to the mapply to be reset with the gsub(...) that OP specified in their comment.

    In general, if you want to access just one of the text labels, there is a very similar structure which I made use of in my mapply:

    num_to_access <- 1
    gg$grobs[which(grepl("strip-t",gg$layout$name))][[num_to_access]][["grobs"]][[1]][["children"]][[2]][["children"]][[1]]$label
    

    So to access the 4th label for example all you would need to do is change num_to_acces to be 4. Hope this helps!