首页 文章

与ggplot2并排绘制的图

提问于
浏览
258

我想使用ggplot2 package并排放置两个图,即相当于 par(mfrow=c(1,2)) .

例如,我想让以下两个图表以相同的比例并排显示 .

x <- rnorm(100)
eps <- rnorm(100,0,.2)
qplot(x,3*x+eps)
qplot(x,2*x+eps)

我需要将它们放在相同的data.frame中吗?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()

12 回答

  • 405

    任意ggplots并排(或网格上的n个图)

    gridExtra包中的函数 grid.arrange() 将组合多个图;这就是你如何并排放置两个 .

    require(gridExtra)
    plot1 <- qplot(1)
    plot2 <- qplot(1)
    grid.arrange(plot1, plot2, ncol=2)
    

    当两个图不是基于相同的数据时,这很有用,例如,如果要在不使用reshape()的情况下绘制不同的变量 .

    这会将输出绘制为副作用 . 要将副作用打印到文件,请指定设备驱动程序(例如 pdfpng 等),例如

    pdf("foo.pdf")
    grid.arrange(plot1, plot2)
    dev.off()
    

    或者,将 arrangeGrob()ggsave() 结合使用,

    ggsave("foo.pdf", arrangeGrob(plot1, plot2))
    

    这相当于使用 par(mfrow = c(1,2)) 制作两个不同的图 . 这不仅节省了安排数据的时间,还需要两个不同的情节 .


    附录:使用Facets

    方面有助于为不同的组制作类似的图 . 这在下面的许多答案中都有所指出,但我想用与上述图相当的例子来强调这种方法 .

    mydata <- data.frame(myGroup = c('a', 'b'), myX = c(1,1))
    
    qplot(data = mydata, 
        x = myX, 
        facets = ~myGroup)
    
    ggplot(data = mydata) + 
        geom_bar(aes(myX)) + 
        facet_wrap(~myGroup)
    

    更新

    cowplot中的 plot_grid 函数值得一试作为 grid.arrange 的替代方案 . 请参阅下面的@ claus-wilke的answerthis vignette的等效方法;但该功能允许根据this vignette对绘图位置和大小进行更精细的控制 .

  • 1

    Update: 这个答案很老了 . gridExtra::grid.arrange() 现在是推荐的方法 . 我把它留在这里以防它可能有用 .


    Stephen Turner posted the arrange() function on Getting Genetics Done blog(有关申请说明,请参阅帖子)

    vp.layout <- function(x, y) viewport(layout.pos.row=x, layout.pos.col=y)
    arrange <- function(..., nrow=NULL, ncol=NULL, as.table=FALSE) {
     dots <- list(...)
     n <- length(dots)
     if(is.null(nrow) & is.null(ncol)) { nrow = floor(n/2) ; ncol = ceiling(n/nrow)}
     if(is.null(nrow)) { nrow = ceiling(n/ncol)}
     if(is.null(ncol)) { ncol = ceiling(n/nrow)}
            ## NOTE see n2mfrow in grDevices for possible alternative
    grid.newpage()
    pushViewport(viewport(layout=grid.layout(nrow,ncol) ) )
     ii.p <- 1
     for(ii.row in seq(1, nrow)){
     ii.table.row <- ii.row 
     if(as.table) {ii.table.row <- nrow - ii.table.row + 1}
      for(ii.col in seq(1, ncol)){
       ii.table <- ii.p
       if(ii.p > n) break
       print(dots[[ii.table]], vp=vp.layout(ii.table.row, ii.col))
       ii.p <- ii.p + 1
      }
     }
    }
    
  • 15

    使用 tidyverse

    x <- rnorm(100)
    eps <- rnorm(100,0,.2)
    df <- data.frame(x, eps) %>% 
      mutate(p1 = 3*x+eps, p2 = 2*x+eps) %>% 
      tidyr::gather("plot", "value", 3:4) %>% 
      ggplot(aes(x = x , y = value))+ geom_point()+geom_smooth()+facet_wrap(~plot, ncol =2)
    
    df
    

    enter image description here

  • 17

    cowplot 包以适合发布的方式为您提供了一种很好的方法 .

    x <- rnorm(100)
    eps <- rnorm(100,0,.2)
    A = qplot(x,3*x+eps, geom = c("point", "smooth"))+theme_gray()
    B = qplot(x,2*x+eps, geom = c("point", "smooth"))+theme_gray()
    cowplot::plot_grid(A, B, labels = c("A", "B"), align = "v")
    

    enter image description here

  • 8

    是的,您需要妥善安排数据 . 一种方法是:

    X <- data.frame(x=rep(x,2),
                    y=c(3*x+eps, 2*x+eps),
                    case=rep(c("first","second"), each=100))
    
    qplot(x, y, data=X, facets = . ~ case) + geom_smooth()
    

    我相信在plyr或重塑方面有更好的技巧 - 我仍然没有真正掌握Hadley所有这些强大的包装 .

  • 44

    使用patchwork包,您只需使用 + 运算符:

    # install.packages("devtools")
    devtools::install_github("thomasp85/patchwork")
    
    library(ggplot2)
    p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
    p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
    
    library(patchwork)
    p1 + p2
    

    patchwork

  • 8

    ggplot2基于网格图形,它提供了一种不同的系统,用于在页面上排列图形 . par(mfrow...) 命令没有直接等效命令,因为网格对象(称为grobs)不一定立即绘制,但可以在转换为图形输出之前作为常规R对象进行存储和操作 . 这比现在的基本图形模型的绘制更具灵活性,但策略必然略有不同 .

    我写了 grid.arrange() 来提供一个尽可能接近 par(mfrow) 的简单接口 . 在最简单的形式中,代码看起来像:

    library(ggplot2)
    x <- rnorm(100)
    eps <- rnorm(100,0,.2)
    p1 <- qplot(x,3*x+eps)
    p2 <- qplot(x,2*x+eps)
    
    library(gridExtra)
    grid.arrange(p1, p2, ncol = 2)
    

    enter image description here

    this vignette中详细介绍了更多选项 .

    一个常见的抱怨是情节不一定是对齐的,例如:当他们有不同大小的轴标签时,这是设计的: grid.arrange 不会尝试特殊情况ggplot2对象,并将它们平等地对待其他grobs(例如,格子图) . 它只是将凹凸放置在矩形布局中 .

    对于ggplot2对象的特殊情况,我编写了另一个函数 ggarrange ,它具有类似的界面,它试图对齐绘图面板(包括刻面图)并尝试在用户定义时考虑纵横比 .

    library(egg)
    ggarrange(p1, p2, ncol = 2)
    

    这两个功能都与 ggsave() 兼容 . 有关不同选项和一些历史背景的一般概述,this vignette offers additional information .

  • 0

    还有multipanelfigure package值得一提 . 另见answer .

    library(ggplot2)
    theme_set(theme_bw())
    
    q1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
    q2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
    q3 <- ggplot(mtcars) + geom_smooth(aes(disp, qsec))
    q4 <- ggplot(mtcars) + geom_bar(aes(carb))
    
    library(magrittr)
    library(multipanelfigure)
    figure1 <- multi_panel_figure(columns = 2, rows = 2, panel_label_type = "none")
    # show the layout
    figure1
    

    figure1 %<>%
      fill_panel(q1, column = 1, row = 1) %<>%
      fill_panel(q2, column = 2, row = 1) %<>%
      fill_panel(q3, column = 1, row = 2) %<>%
      fill_panel(q4, column = 2, row = 2)
    figure1
    

    # complex layout
    figure2 <- multi_panel_figure(columns = 3, rows = 3, panel_label_type = "upper-roman")
    figure2
    

    figure2 %<>%
      fill_panel(q1, column = 1:2, row = 1) %<>%
      fill_panel(q2, column = 3, row = 1) %<>%
      fill_panel(q3, column = 1, row = 2) %<>%
      fill_panel(q4, column = 2:3, row = 2:3)
    figure2
    

    reprex package(v0.2.0.9000)创建于2018-07-06 .

  • 106

    基于 grid.arrange 的解决方案的一个缺点是,它们很难用大多数期刊所需的字母(A,B等)标记图 .

    我写了cowplot包来解决这个(以及其他一些)问题,特别是函数 plot_grid()

    library(cowplot)
    
    iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
      geom_boxplot() + theme_bw()
    
    iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
      geom_density(alpha = 0.7) + theme_bw() +
      theme(legend.position = c(0.8, 0.8))
    
    plot_grid(iris1, iris2, labels = "AUTO")
    

    enter image description here

    plot_grid() 返回的对象是另一个ggplot2对象,您可以像往常一样使用 ggsave() 保存它:

    p <- plot_grid(iris1, iris2, labels = "AUTO")
    ggsave("plot.pdf", p)
    

    或者,你可以使用牛皮图功能 save_plot() ,这是一个围绕 ggsave() 的薄包装,可以很容易地获得组合图的正确尺寸,例如:

    p <- plot_grid(iris1, iris2, labels = "AUTO")
    save_plot("plot.pdf", p, ncol = 2)
    

    ncol = 2 参数告诉 save_plot() 并排有两个图, save_plot() 使保存的图像宽两倍 . )

    有关如何在网格中排列绘图的更深入描述,请参阅this vignette.还有一个插图解释如何使用shared legend.绘制绘图

    一个常见的混淆点是cowplot包改变了默认的ggplot2主题 . 包的行为是这样的,因为它最初是为内部实验室使用而编写的,我们从不这样做使用默认主题 . 如果这会导致问题,您可以使用以下三种方法之一来解决它们:

    1.为每个绘图手动设置主题 . 我认为总是为每个情节指定一个特定的主题是很好的做法,就像我在上面的例子中使用 + theme_bw() 一样 . 如果指定特定主题,则默认主题无关紧要 .

    2.将默认主题恢复为ggplot2默认值 . 您可以使用一行代码执行此操作:

    theme_set(theme_gray())
    

    3.在不附加包装的情况下调用cowplot功能 . 您也不能调用 library(cowplot)require(cowplot) 而是通过添加 cowplot:: 来调用cowplot函数 . 例如,使用ggplot2默认主题的上述示例将变为:

    ## Commented out, we don't call this
    # library(cowplot)
    
    iris1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
      geom_boxplot()
    
    iris2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
      geom_density(alpha = 0.7) +
      theme(legend.position = c(0.8, 0.8))
    
    cowplot::plot_grid(iris1, iris2, labels = "AUTO")
    

    enter image description here

    Update: As of ggplot2 3.0.0, plots can be labeled directly, see e.g. here.

  • 2

    您可以使用Winston Chang's R cookbook中的以下 multiplot 函数

    multiplot(plot1, plot2, cols=2)
    

    multiplot <- function(..., plotlist=NULL, cols) {
        require(grid)
    
        # Make a list from the ... arguments and plotlist
        plots <- c(list(...), plotlist)
    
        numPlots = length(plots)
    
        # Make the panel
        plotCols = cols                          # Number of columns of plots
        plotRows = ceiling(numPlots/plotCols) # Number of rows needed, calculated from # of cols
    
        # Set up the page
        grid.newpage()
        pushViewport(viewport(layout = grid.layout(plotRows, plotCols)))
        vplayout <- function(x, y)
            viewport(layout.pos.row = x, layout.pos.col = y)
    
        # Make each plot, in the correct location
        for (i in 1:numPlots) {
            curRow = ceiling(i/plotCols)
            curCol = (i-1) %% plotCols + 1
            print(plots[[i]], vp = vplayout(curRow, curCol ))
        }
    
    }
    
  • 2

    使用reshape包你可以做这样的事情 .

    library(ggplot2)
    wide <- data.frame(x = rnorm(100), eps = rnorm(100, 0, .2))
    wide$first <- with(wide, 3 * x + eps)
    wide$second <- with(wide, 2 * x + eps)
    long <- melt(wide, id.vars = c("x", "eps"))
    ggplot(long, aes(x = x, y = value)) + geom_smooth() + geom_point() + facet_grid(.~ variable)
    
  • 16

    如果您想使用循环绘制多个ggplot图(例如,如此处所述:Creating multiple plots in ggplot with different Y-axis values using a loop),上述解决方案可能效率不高,这是分析未知(或大)数据集的理想步骤(例如,当您想要plot计算数据集中所有变量的计数 .

    下面的代码显示了如何使用上面提到的'multiplot()'来做到这一点,其来源是:http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2)

    plotAllCounts <- function (dt){   
      plots <- list();
      for(i in 1:ncol(dt)) {
        strX = names(dt)[i]
        print(sprintf("%i: strX = %s", i, strX))
        plots[[i]] <- ggplot(dt) + xlab(strX) +
          geom_point(aes_string(strX),stat="count")
      }
    
      columnsToPlot <- floor(sqrt(ncol(dt)))
      multiplot(plotlist = plots, cols = columnsToPlot)
    }
    

    现在运行该函数 - 获取在一页上使用ggplot打印的所有变量的计数

    dt = ggplot2::diamonds
    plotAllCounts(dt)
    

    需要注意的一点是:
    使用 aes(get(strX)) ,在使用 ggplot 时通常在循环中使用,在上面的代码而不是 aes_string(strX) 中将不会绘制所需的图 . 相反,它会多次绘制最后一个情节 . 我还没弄清楚为什么 - 它可能需要做 aes 并在 ggplot 中调用 aes_string .

    否则,希望你会发现这个功能很有用 .

相关问题