Positioning of grobs

460 views Asked by At

I want to plot data for a linear model in a main plot and a plot of the effects (forest plot) as a subplot using arrangeGrob.

Here are the data:

set.seed(1)
main.df <- data.frame(sample=c(paste("E.plus.A.plus",1:3,sep="_"),paste("E.minus.A.plus",1:3,sep="_"),paste("E.plus.A.minus",1:3,sep="_"),paste("E.minus.A.minus",1:3,sep="_")),
                      replicate=rep(1:3,4),cpm=c(rnorm(12)),
                      factor.level=factor(c(rep("E.plus.A.plus",3),rep("E.minus.A.plus",3),rep("E.plus.A.minus",3),rep("E.minus.A.minus",3)),
                                    levels=c("E.plus.A.plus","E.minus.A.plus","E.plus.A.minus","E.minus.A.minus")))

effects.df <- data.frame(factor.level=c("E.plus.A.plus-E.minus.A.plus","E.plus.A.plus-E.plus.A.minus","E.plus.A.plus-E.minus.A.minus",
                                        "E.minus.A.plus-E.plus.A.minus","E.minus.A.plus-E.minus.A.minus","E.plus.A.minus-E.minus.A.minus"),
                         effect=rnorm(6),effect.df=runif(6,0,0.5),p.value=runif(6,0,1),y=1:6+0.2)
effects.df$effect.high <- effects.df$effect+effects.df$effect.df
effects.df$effect.low <- effects.df$effect-effects.df$effect.df
effects.df$factor.level <- factor(effects.df$factor.level,levels=effects.df$factor.level)

The ggplots:

require(ggplot2)
require(grid)
require(gridExtra)
main.plot <- ggplot(main.df,aes(x=replicate,y=cpm,color=factor.level))+geom_point(size=3)+
  facet_wrap(~factor.level,ncol=length(levels(main.df$factor.level)))+
  labs(x="replicate",y="cpm")+scale_x_continuous(breaks=unique(main.df$replicate))+theme_bw()+
  theme(legend.key=element_blank(),panel.border=element_blank(),strip.background=element_blank(),axis.title=element_text(size=8),plot.title=element_text(size=9,hjust=0.5))

Which is: enter image description here

sub.plot <- ggplot(effects.df,aes(x=effect,y=factor.level,color=factor.level))+geom_point(size=2.5,shape=19)+geom_errorbarh(aes(xmax=effect.high,xmin=effect.low),height=0.1)+
  geom_vline(xintercept=0,linetype="longdash",colour="black",size=0.25)+theme_bw()+theme(legend.key=element_blank(),panel.border=element_blank(),strip.background=element_blank(),axis.title=element_text(size=7),axis.text=element_text(size=7),legend.text=element_text(size=7),legend.title=element_text(size=7))+
  geom_text(aes(x=effects.df$effect,y=effects.df$y,label=format(signif(effects.df$p.value,2),scientific=T)),size=2.5)

And is: enter image description here

And here's how I try to combine them into a single plot:

if(!is.null(dev.list())) dev.off()
blank <- grid.rect(gp = gpar(col = "white"))
sub.plot.grob <- arrangeGrob(blank,sub.plot,ncol=1)
combined.plot <- arrangeGrob(main.plot,sub.plot,ncol=2,widths=c(1,1))
grid.arrange(combined.plot)

which gives: enter image description here

How do I adjust the position and dimensions so that sub.plot is smaller (all layers, e.g., text are reduced proportionally), and is positioned below the legend of main.plot?

2

There are 2 answers

0
Mark Peterson On BEST ANSWER

I strongly recommend the package cowplot for this sort of task. Here, I am building three nested sets (the main plot to the left, then the two legends together at the top right, then the sub plot at the bottom right). Note the wonderful get_legend function that make pulling the legends incredibly easy.

plot_grid(
  main.plot + theme(legend.position = "none")
  , plot_grid(
    plot_grid(
      get_legend(main.plot)
      , get_legend(sub.plot)
      , nrow = 1
    )
    , sub.plot + theme(legend.position = "none")
    , nrow = 2
  )
  , nrow = 1
  )

gives:

enter image description here

Obviously I'd recommend changing one (or both) of the color palettes, but that should give what you want.

If you really want the legend with the sub.plot, instead of with the other legend, you could skip the get_legend.

You can also adjust the width/height of the sets using rel_widths and rel_heights if you want something other than the even sizes.

As an additional note, cowplot sets its own default theme on load. I generally revert to what I like by running theme_set(theme_minimal()) right after loading it.

0
baptiste On

here's a grid.arrange solution,

grid.arrange(grobs = replicate(4, ggplot(), simplify = FALSE), 
             layout_matrix = cbind(c(1,1), c(3,2), c(4, 2)), 
             widths = c(2,1,1))

enter image description here

with those bits and pieces,

get_legend <- function(p) {
   g <- ggplotGrob(p)
   id <- grep("guide", g$layout$name)
   g$grobs[[id]]
}

leg1 <- get_legend(main.plot); leg2 <- get_legend(sub.plot)
gl <- list(main.plot + theme(legend.position = "none"), 
           sub.plot + theme(legend.position = "none"), leg1, leg2)

grid.arrange(grobs = gl, 
             layout_matrix = cbind(c(1,1), c(3,2), c(4, 2)), 
             widths = c(2,1,1))

enter image description here