Search code examples
rggplot2resizefacet-wrap

Manually define size of frame using ggplot2 and geom_boxplot


How can I manually define the size of the frames of individual graphs when plotting multiple boxplots using ggplot2?

If you e.g. have a 2x2 plot and each of the frames should be quadratic with 400x400 pixel height and width, you cannot set the dimension of the entire plot to 800x800 since there are still the axis texts (and these may be different in length due to the "scales="free_y" command in facet_wrap(~variable,scales="free_y")). This leads to "squeezed" instead of quadratic frame sizes.

p<-ggplot(data=data.frame,aes(x=x,y=value))+
geom_boxplot(size=0.1, fill="grey90")+
geom_point(aes(x=x, y=value, fill="black"),inherit.aes=FALSE, colour="red", size=2, position=position_jitterdodge(jitter.width = 0.25), show.legend=FALSE)+
facet_wrap(~variable,scales="free_y")+
theme_bw()

Expected result: graph frames should always be quadratic with height and width defined by the user.


Solution

  • You could do this with gtables in the following way. First we'll build a plot.

    library(ggplot2)
    library(grid)
    # I don't have your data so I'll just use the diamonds dataset
    p <- ggplot(diamonds, aes(x = cut, y = price)) +
      geom_boxplot() +
      facet_wrap(~ clarity, scales = "free_y")
    

    Then we'll convert the plot to a gtable, get the panel positions and adjust the widths and heights to our liking.

    gt <- ggplotGrob(p)
    # Usually panels don't span multiple cells in the table, so we can take the
    # left ("l") and top ("t") positions of the panels.
    panel_x <- panel_cols(gt)[,"l"]
    panel_y <- panel_rows(gt)[,"t"]
    gt$widths[panel_x]  <- unit(400/72, "inches")
    gt$heights[panel_y] <- unit(400/72, "inches")
    
    grid.newpage(); grid.draw(gt)
    

    Unfortunately the unit() function doesn't let you specify dimensions in pixels, so I've assumed that the resolution of your output would be 72 pixels per inch. Have a look at ?unit to see the options.

    EDIT:

    This is what the above looks like (with unit(100/72, "inches") for brevity):

    enter image description here

    Now if you also want the strips (the text that look like panel titles in a darker box) to be included in the 400x400 box, you might want to find the strip positions along the rows and subtract these from the units you supply.

    gt <- ggplotGrob(p)
    panel_x <- panel_cols(gt)[,"l"]
    panel_y <- panel_rows(gt)[,"t"]
    strip_y <- unique(gt$layout$t[grepl("strip", gt$layout$name)])
    gt$widths[panel_x]  <- unit(100/72, "inches")
    gt$heights[panel_y] <- unit(100/72, "inches") - gt$heights[strip_y]
    
    grid.newpage(); grid.draw(gt)
    

    Which looks like the following:

    enter image description here