Align plot areas in ggplot

前端 未结 5 1593
無奈伤痛
無奈伤痛 2020-11-29 01:04

I am trying to use grid.arrange to display multiple graphs on the same page generated by ggplot. The plots use the same x data but with different y variables. The plots come

相关标签:
5条回答
  • 2020-11-29 01:12

    Edit

    Simpler solutions are: 1) use the cowplot package (see answer here); or 2) use egg package available on github.

    # devtools::install_github("baptiste/egg")
    library(egg)
    library(grid)
    
    g = ggarrange(x1, x2, x3, x4, ncol = 2)
    grid.newpage()
    grid.draw(g)
    

    Original

    Minor edit: Updating code.

    If you want to keep the axis labels, then with some fiddling, and borrowing code from here, this does the job.

    library(ggplot2)
    library(gtable)
    library(grid)
    library(gridExtra)
    
    # Get the widths
    gA <- ggplotGrob(x1)
    gB <- ggplotGrob(x2)
    gC <- ggplotGrob(x3)
    gD <- ggplotGrob(x4)
    maxWidth = unit.pmax(gA$widths[2:3], gB$widths[2:3], 
                         gC$widths[2:3], gD$widths[2:3])
    
    # Set the widths
    gA$widths[2:3] <- maxWidth
    gB$widths[2:3] <- maxWidth
    gC$widths[2:3] <- maxWidth
    gD$widths[2:3] <- maxWidth
    
    # Arrange the four charts
    grid.arrange(gA, gB, gC, gD, nrow=2)
    

    enter image description here

    ALTERNATIVE SOLUTIONS: There are rbind and cbind functions in the gtable package for combining grobs into one grob. For the charts here, the widths should be set using size = "max", but the CRAN version of gtable throws an error.

    One option is to examine the grid.arrange plot, then use size = "first" or size = "last"` options:

    # Get the ggplot grobs
    gA <- ggplotGrob(x1)  
    gB <- ggplotGrob(x2)
    gC <- ggplotGrob(x3)
    gD <- ggplotGrob(x4)
    
    # Arrange the four charts
    grid.arrange(gA, gB, gC, gD, nrow=2)
    
    # Combine the plots   
    g = cbind(rbind(gA, gC, size = "last"), rbind(gB, gD, size = "last"), size = "first")
    
    # draw it
    grid.newpage()
    grid.draw(g)
    

    A second option is to binding functions from gridExtra package.

    # Get the ggplot grobs
    gA <- ggplotGrob(x1)  
    gB <- ggplotGrob(x2)
    gC <- ggplotGrob(x3)
    gD <- ggplotGrob(x4)
    
    # Combine the plots
    g = cbind.gtable(rbind.gtable(gA, gC, size = "max"), rbind.gtable(gB, gD, size = "max"), size = "max")
    
    # Draw it
    grid.newpage()
    grid.draw(g)
    
    0 讨论(0)
  • 2020-11-29 01:16

    That's exactly the kind of problem for which I wrote the cowplot package. It can be done in one line in that package:

    require(cowplot) # loads ggplot2 as dependency
    # re-create the four plots
    A <- c(1,5,6,7,9)
    B <- c(10,56,64,86,98)
    C <- c(2001,3333,5678,4345,5345)
    D <- c(13446,20336,24333,34345,42345)
    L <- c(20,34,45,55,67)
    M <- data.frame(L, A, B, C, D)
    x1 <- ggplot(M, aes(L, A,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
    x2 <- ggplot(M, aes(L, B,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
    x3 <- ggplot(M, aes(L, C,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
    x4 <- ggplot(M, aes(L, D,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
    
    # arrange into grid and align
    plot_grid(x1, x2, x3, x4, align='vh')
    

    This is the result: enter image description here (Note that cowplot changes the default ggplot2 theme. You can get the gray one back though if you really want to.)

    As a bonus feature, you can also add plot labels in the top-left corner of each graph:

    plot_grid(x1, x2, x3, x4, align='vh', labels=c('A', 'B', 'C', 'D'))
    

    Result: enter image description here

    I use the labels option on virtually every multi-part graph I make.

    0 讨论(0)
  • 2020-11-29 01:24

    If you are using RMarkdown and knitting to PDF, I have an alternative approach. Knitr offers the functionality to plot subfigures when creating a PDF, which allows you to have multiple figures in a plot, each with their own caption.

    For this to work, each plot has to be displayed separately. By joining together several functions from the cowplot package, I made the following function which aligns plots whilst keeping them as separate objects:

    plot_grid_split <- function(..., align = "hv", axis= "tblr"){
      aligned_plots <- cowplot::align_plots(..., align=align, axis=axis)
      plots <- lapply(1:length(aligned_plots), function(x){
        cowplot::ggdraw(aligned_plots[[x]])
      })
      invisible(capture.output(plots))
    }
    

    Here is an example, comparing the layout normally vs using the function:

    ---
    output: pdf_document
    header-includes:
       - \usepackage{subfig}
    ---
    
    ```{r}
    plot_grid_split <- function(..., align = "hv", axis= "tblr"){
      aligned_plots <- cowplot::align_plots(..., align=align, axis=axis)
      plots <- lapply(1:length(aligned_plots), function(x){
        cowplot::ggdraw(aligned_plots[[x]])
      })
      invisible(capture.output(plots))
    }
    ```
    
    ```{r fig-sub, fig.cap='Four Plots Not Aligned', fig.subcap=c('Plot One', 'Plot Two', 'Plot Three', 'Plot Four'), out.width='.49\\linewidth', fig.asp=1, fig.ncol = 2}  
    library(ggplot2) 
    plot <- ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Species)) +
      geom_point()
    
    plot + labs(title = "A nice title")
    plot + labs(caption = "A sample caption")
    plot + theme(legend.position = "none")
    plot + theme(legend.position = "top")
    ```  
    
    ```{r fig-sub-2, fig.cap='Four Plots Aligned', fig.subcap=c('Plot One', 'Plot Two', 'Plot Three', 'Plot Four'), out.width='.49\\linewidth', fig.asp=1, fig.ncol = 2}
    
    x1 <- plot + labs(title = "A nice title")
    x2 <- plot + labs(caption = "A sample caption")
    x3 <- plot + theme(legend.position = "none")
    x4 <- plot + theme(legend.position = "top")
    
    plot_grid_split(x1, x2, x3, x4)
    ```
    

    • You can learn more about subfigures in R within this post.
    • In addition, you can check out the knitr options to read more about the chunk options for subfigures: https://yihui.name/knitr/options/
    0 讨论(0)
  • 2020-11-29 01:34

    Patchwork is a new package which makes it really easy to format and layout multiple ggplots. One of the best things about it is that it aligns the plot areas automatically. Plus, the syntax is really easy.

    devtools::install_github("thomasp85/patchwork")
    library(patchwork)
    x1 + x2 + x3 + x4 + plot_layout(ncol = 2)
    

    Check out the GitHub page for more examples: https://github.com/thomasp85/patchwork

    0 讨论(0)
  • 2020-11-29 01:38

    I would use faceting for this problem:

    library(reshape2)
    dat <- melt(M,"L") # When in doubt, melt!
    
    ggplot(dat, aes(L,value)) + 
    geom_point() + 
    stat_smooth(method="lm") + 
    facet_wrap(~variable,ncol=2,scales="free")
    

    Example

    Note: The layman may miss that the scales are different between facets.

    0 讨论(0)
提交回复
热议问题