【问题标题】:Side-by-side plots with ggplot2使用 ggplot2 并排绘图
【发布时间】:2016-01-01 23:08:32
【问题描述】:

我想使用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()

【问题讨论】:

标签: r ggplot2 visualization r-faq


【解决方案1】:

根据我的经验,gridExtra:grid.arrange 可以完美地工作,如果您尝试在循环中生成图。

短代码片段:

gridExtra::grid.arrange(plot1, plot2, ncol = 2)

** 更新此评论以展示如何在 for 循环中使用 grid.arrange() 为分类变量的不同因子生成图。

for (bin_i in levels(athlete_clean$BMI_cat)) {

plot_BMI <- athlete_clean %>% filter(BMI_cat == bin_i) %>% group_by(BMI_cat,Team) %>% summarize(count_BMI_team = n()) %>% 
          mutate(percentage_cbmiT = round(count_BMI_team/sum(count_BMI_team) * 100,2)) %>% 
          arrange(-count_BMI_team) %>% top_n(10,count_BMI_team) %>% 
          ggplot(aes(x = reorder(Team,count_BMI_team), y = count_BMI_team, fill = Team)) +
            geom_bar(stat = "identity") +
            theme_bw() +
            # facet_wrap(~Medal) +
            labs(title = paste("Top 10 Participating Teams with \n",bin_i," BMI",sep=""), y = "Number of Athletes", 
                 x = paste("Teams - ",bin_i," BMI Category", sep="")) +
            geom_text(aes(label = paste(percentage_cbmiT,"%",sep = "")), 
                      size = 3, check_overlap = T,  position = position_stack(vjust = 0.7) ) +
            theme(axis.text.x = element_text(angle = 00, vjust = 0.5), plot.title = element_text(hjust = 0.5), legend.position = "none") +
            coord_flip()

plot_BMI_Medal <- athlete_clean %>% 
          filter(!is.na(Medal), BMI_cat == bin_i) %>% 
          group_by(BMI_cat,Team) %>% 
          summarize(count_BMI_team = n()) %>% 
          mutate(percentage_cbmiT = round(count_BMI_team/sum(count_BMI_team) * 100,2)) %>% 
          arrange(-count_BMI_team) %>% top_n(10,count_BMI_team) %>% 
          ggplot(aes(x = reorder(Team,count_BMI_team), y = count_BMI_team, fill = Team)) +
            geom_bar(stat = "identity") +
            theme_bw() +
            # facet_wrap(~Medal) +
            labs(title = paste("Top 10 Winning Teams with \n",bin_i," BMI",sep=""), y = "Number of Athletes", 
                 x = paste("Teams - ",bin_i," BMI Category", sep="")) +
            geom_text(aes(label = paste(percentage_cbmiT,"%",sep = "")), 
                      size = 3, check_overlap = T,  position = position_stack(vjust = 0.7) ) +
            theme(axis.text.x = element_text(angle = 00, vjust = 0.5), plot.title = element_text(hjust = 0.5), legend.position = "none") +
            coord_flip()

gridExtra::grid.arrange(plot_BMI, plot_BMI_Medal, ncol = 2)

}

上面 for 循环中的一个示例图如下所示。 上述循环将为所有级别的 BMI 类别生成多个图。

Sample Image

如果您希望在for 循环中更全面地使用grid.arrange(),请查看https://rpubs.com/Mayank7j_2020/olympic_data_2000_2016

【讨论】:

  • 您的回答与 2017 年 12 月 2 日 4:20 的 baptiste 回答相比有何改进?您的答案似乎是重复的。在这里查看什么是可接受的答案How to Answer
  • 我无法在循环中根据需要划分情节,因此提出了建议。最初,我编写了 for 循环的完整 sn-p 及其实现,但后来决定暂时放弃它。一周左右会更新完整代码。
  • 我一开始就尝试使用cowplot包来实现它,但没有成功。在我的快速浏览中,没有人提到 for 循环中的多个绘图解决方案,因此我发表了评论。如果我错了,请给我任何评论。
  • 如果您的答案中的代码包含一个不同的 for 循环。
  • 我会在一周内更新它在这里的问题和 Kaggle 上的整个项目。干杯。
【解决方案2】:

使用patchwork 包,您可以简单地使用+ 运算符:

library(ggplot2)
library(patchwork)

p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))


p1 + p2

其他运算符包括 / 用于堆叠绘图以并排放置绘图,以及 () 用于对元素进行分组。例如,您可以使用(p1 | p2 | p3) /p 配置3 个图的顶行和一个图的底行。如需更多示例,请参阅package documentation

【讨论】:

  • 为了完整起见,现在 CRAN 上也有拼凑。希望你对我的小编辑感到满意
  • Patchwork 是对图形布局包景观的一个很棒的补充。谢谢!
  • 这些年来我已经使用了大多数其他解决方案,我不得不说这个答案(拼凑)是我迄今为止的首选。
【解决方案3】:

基于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")

plot_grid()返回的对象是另一个ggplot2对象,可以照常用ggsave()保存:

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

或者,您可以使用cowplot 函数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")

更新:

  • 从 cowplot 1.0 开始,默认的 ggplot2 主题不再更改。
  • 从 ggplot2 3.0.0 开始,可以直接标记图,参见例如here.

【讨论】:

  • 在输出的cowplot 中删除了两个图的背景主题?还有其他选择吗?
  • @VAR121 是的,这是一行代码。在介绍性小插曲的第一部分末尾解释:cran.rstudio.com/web/packages/cowplot/vignettes/…
  • 是否可以使用此软件包为所有地块设置相同的 y 比例?
  • 您必须手动设置 y 比例以匹配。或者考虑分面。
  • 您可以在使用 grid.arrange() 之前为每个绘图设置一个 ggtitle() 吗?
【解决方案4】:

使用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

【讨论】:

    【解决方案5】:

    更新:这个答案很老了。 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
      }
     }
    }
    

    【讨论】:

    • 它基本上是grid.arrange 的一个非常过时的版本(希望我当时没有将它发布在邮件列表中——没有办法更新这些在线资源),打包的版本是如果你问我更好的选择
    【解决方案6】:

    还有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 年 7 月 6 日创建。

    【讨论】:

      【解决方案7】:

      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)
      

      更多选项详见this vignette

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

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

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

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

      【讨论】:

        【解决方案8】:

        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")
        

        【讨论】:

        【解决方案9】:

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

        下面的代码展示了如何使用上面提到的'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) 将不会绘制所需的图。相反,它将多次绘制最后一个情节。我还没弄清楚为什么 - 它可能必须在 ggplot 中调用 aesaes_string

        否则,希望您会发现该功能很有用。

        【讨论】:

        【解决方案10】:

        并排的任何 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)) 制作两个不同的图。这不仅节省了排列数据的时间,而且在您想要两个不同的图时很有必要。


        附录:使用构面

        分面有助于为不同的组制作类似的图。在下面的许多答案中都指出了这一点,但我想通过与上述图表等效的示例来强调这种方法。

        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 对绘图位置和大小进行更精细的控制。

        【讨论】:

        • 当我使用 ggplot 对象运行您的代码时,sidebysideplot 为空。如果要将输出保存到文件,请使用 gridArrange。见stackoverflow.com/questions/17059099/…
        • @Jim 感谢您指出这一点。我已经修改了我的答案。如果还有任何问题,请告诉我。
        • grid.aarange 现在被弃用了吗?
        • ?grid.arrange 让我觉得这个函数现在叫做arrangeGrob。我可以通过a &lt;- arrangeGrob(p1, p2) 然后print(a) 来做我想做的事。
        • @blakeoft 你看过这些例子吗? grid.arrange 仍然是一个有效的、未弃用的函数。您是否尝试使用该功能?如果不符合您的预期,会发生什么。
        【解决方案11】:

        您可以使用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 ))
            }
        
        }
        

        【讨论】:

          【解决方案12】:

          使用 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)
          

          【讨论】:

            【解决方案13】:

            是的,我认为您需要适当地安排数据。一种方法是:

            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 或 reshape 有更好的技巧——我仍然没有真正跟上速度 Hadley 的所有这些强大的软件包。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-04-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-12-04
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多