3

I am trying to arrange 2 plots created using ggplot2 and would like the plots to be the size of a square and one next to the other, with the common legend next to them, so that the image fits nicely in a portrait style page. The problem is when I use grid.arrange to arrange the Grobs the labels and legends become tiny, and the plot space huge.

There must be something I am missing about the grid.arrange package, like a command to set all the ratios correctly, but I have tried so many different combinations of text sizes and have tried setting the sizes of the plots, but nothing seems to work. Can you please help me?

x1 <- c(10,20,30,40,50,60,70,80)
x2 <- c(20,40,60,80)
y1 <- c(10,30,100,30,20,40,20,10)
y2 <- c(20,60,20,30)
z1 <- c(1,1,1,2,2,2,2,2)
z2 <- c(2,2,1,2,1,2,1,2)
df1 <- data.frame(x=x1,y=y1, z=z1)
df2 <- data.frame(x=x2,y=y2, z=z1)

p1<-  ggplot(df1, aes(x=x,
     y=y,
     group=z,
     colour=z)) +
     geom_point() + theme_bw() +
     xlab("The size of this is tiny using grid.arrange")  +ylab("The size of this is tiny using grid.arrange") + ggtitle("A") + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), plot.title=element_text(size=16, hjust=0), axis.title = element_text(size=14), axis.text= element_text(size=12), legend.text= element_text(size = 12), legend.title = element_text(size = 12)) +
     geom_abline(intercept = 0, slope = 1) +
     xlim(0, 100) + ylim(0, 100) + guides(colour = guide_legend("\n This is a \n multi-line legend title", override.aes = list(size = 10)))

p2<-  ggplot(df2, aes(x=x,
     y=y,
     group=z,
     colour=z)) +
     geom_point() + theme_bw() +
     xlab("The size of this is tiny using grid.arrange")  +ylab("The size of this is tiny using grid.arrange") + ggtitle("B") + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), plot.title=element_text(size=16, hjust=0), axis.title = element_text(size=14), axis.text= element_text(size=12), legend.text= element_text(size = 12), legend.title = element_text(size = 12)) +
     geom_abline(intercept = 0, slope = 1) +
     xlim(0, 100) + ylim(0, 100) + guides(colour = guide_legend("\n This is a \n multi-line legend title", override.aes = list(size = 10)))

g_legend<-function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}

mylegend<-g_legend(p1)


pdf("my.figure.pdf", height = 20, width = 30) ## I have tried many different values for this too

grid.arrange(arrangeGrob(p1 + theme(legend.position="none"), # widths = unit(20, "cm"), heights = unit(20, "cm"),
p2 + theme(legend.position="none"), # widths = unit(20, "cm"),heights = unit(20, "cm"),
main = textGrob("The main figure title is also small and hard to align left",  hjust =0.6, gp = gpar(fontsize = 22)), nrow=1, heights=rep(10,10)), # widths = unit(10, "cm"),heights = unit(4, "cm")),
mylegend, widths=unit.c(unit(1, "npc") - sum(mylegend$width), sum(mylegend$width)), nrow=1)

dev.off()
dpel
  • 1,954
  • 1
  • 21
  • 31
user971102
  • 3,005
  • 4
  • 30
  • 37
  • +1 The code is a little long to read through, but a good reproducible example. – Simon O'Hanlon Apr 30 '13 at 11:31
  • your pdf file is 20x30 inches big, so the default fontsize (typically about 12 points) will make the text unreadable. Do you need such a large output? – baptiste Apr 30 '13 at 11:48
  • Hi Baptiste, I don't need such a large output (I just need to fit the image in the default page), but I need the two plots to be square and it looked like adjusting the pdf size was helping…Is there a simple way to do this that I am not seeing? Thank you for your help. – user971102 Apr 30 '13 at 12:31

2 Answers2

4

Add a theme() to each plot and set the size of the elements you want to change, e.g

theme( axis.text.x=element_text(size=20) ,  axis.title.x = element_text(size = 20 , colour = "red" ) )

enter image description here

You can try arranging things on a grid page using pushViewport to manually set the viewports like so, but you will probably have to play around with it a bit to get it exactly how you want:

grid.newpage()
# Create a grid arangement of viewports to 'put' the plots into...
# widths and heights are normalised parent coordinates which scale from 0 to 1 with 1 being the entire width of the plot page
# The respect argument forces widths and heights to respect each other as they are set
pushViewport( viewport( layout = grid.layout( 2 , 3 , heights = unit( c( 0.02 , 0.45 ) , "npc" ) , widths = unit( c( 0.4 , 0.4 , 0.1 ) , "npc" ) , respect = matrix(rep(1,6),2) ) ) ) 
# We print plots to particular 'cells' on our page specified by the layout.pos.row and layout.pos.col
print( p1 + theme(legend.position="none") , vp = viewport( layout.pos.row = 2 , layout.pos.col = 1 ) )
print( p2 + theme(legend.position="none") , vp = viewport( layout.pos.row = 2, layout.pos.col = 2 ) )
# The grid.text is output to row one, and breaks across columns 1:2
grid.text("The main figure title is also small and hard to align left"  , just = "left" , x = unit(0.01, "npc"), y = unit(0.5, "npc"), vp = viewport( layout.pos.row = 1, layout.pos.col = 1:2) )
# We use upViewport to go up a level to the parent viewport
upViewport(0)
# We then define a new viewport for the legend which is a table grob.
# I had difficulty with this one so we set x and y coordinates and make it narrow but tall (0.1 npc width and 0.75 noc height)
vp3 <- viewport( width = unit(0.1,"npc") , height = unit(0.75 ,"npc") , x = 0.93, y = .5)
# We then activate this viewport
pushViewport(vp3)
# we output the legend which is a tableGrob to this viewport
grid.draw( mylegend )
popViewport()
Simon O'Hanlon
  • 58,647
  • 14
  • 142
  • 184
  • Hi SimonO101, thank you for replying but this still does not work and the images within grobArrange look the same... – user971102 Apr 30 '13 at 12:29
  • @user971102 I added some code you can play around with to push plots to specific veiewports which you can manually set the size of. – Simon O'Hanlon Apr 30 '13 at 12:55
  • I have never used pushViewport so I don't know how to have one legend for both plots for example or if this is possible, but I'll look into this if there is no way to do it with grid.arrange… I thought this would be a simpler mistake from my part using grid.arrange… – user971102 Apr 30 '13 at 13:08
  • Thank you SimonO101. I will upvote, This works, I just need to make all the plots bigger since they don't fill the image space and add more spacing between them since the x-axis don't fit, so just trying to understand which heights specification belong to the plots. If I can ask you just one more favour, could you add a few comments just to understand your code please? I have to say I am disappointed by grid.arrange while viewport seems to be a great option for this so I will dedicate time to understanding this. Thank you! – user971102 Apr 30 '13 at 14:01
2

I'm not sure what you expect for the output, but try this,

g = arrangeGrob(
  arrangeGrob(p1 + coord_fixed() + theme(legend.position="none"),
              p2 + coord_fixed() +theme(legend.position="none"),
              main = textGrob("The main figure title is also small and hard to align left",  x=0, hjust =0, vjust=1, gp = gpar(fontsize = 22)), nrow=1), 
             mylegend, widths=unit.c(unit(1, "npc") - sum(mylegend$width), 
                                     sum(mylegend$width)), nrow=1)

ggsave("layout.pdf", g, width=12, height=5)
baptiste
  • 75,767
  • 19
  • 198
  • 294
  • Yes! I thought it was odd that it wasn't possible with arrangeGrob! The ratios are still a bit off so I think at the end I'll go with the other method, but thank you for posting this solution, I was going crazy! – user971102 Apr 30 '13 at 17:28
  • 2
    `arrangeGrob` is just a thin wrapper around grid viewports, here your problem was most likely to know how to set a fixed aspect ratio for `ggplot2` and setting the device size. – baptiste Apr 30 '13 at 17:32