使用 ggplot2 的并排绘图

Side-by-side plots with ggplot2

提问人:Christopher DuBois 提问时间:8/9/2009 最后编辑:Gregor ThomasChristopher DuBois 更新时间:8/22/2022 访问量:663411

问:

我想使用 ggplot2 包并排放置两个图,即执行相当于 .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()
ggplot2 可视化 R-FAQ

评论

0赞 JD Long 8/9/2009
我想你也许可以用格子来做到这一点。ggplot2 是硬性要求吗?
8赞 Christopher DuBois 8/11/2009
不。但是我已经花时间调整了 qplots,所以这只是我喜欢的方式。:-)我正在尝试玩 ggplot。
1赞 Boern 4/27/2017
查看 stackoverflow.com/questions/31798162/...
1赞 Henrik 9/26/2017
有关很好的概述,请参阅鸡蛋包装的小插图:在页面上布置多个绘图
0赞 Dan Tarr 5/3/2022
对于最基本的绘图之外的任何内容,您应该使用 ggplot 而不是 qplot

答:

19赞 Dirk is no longer here 8/9/2009 #1

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

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 的所有这些强大的软件包上。

17赞 Thierry 8/10/2009 #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)
10赞 Jeromy Anglim 4/28/2010 #3

更新:这个答案很古老。 现在是推荐的方法。 我把它留在这里,以防万一它可能有用。gridExtra::grid.arrange()


Stephen Turner Getting Genetics Done 博客上发布了 arrange() 函数(有关应用说明,请参阅帖子)

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
  }
 }
}

评论

10赞 baptiste 6/10/2012
它基本上是一个非常过时的版本(希望我当时没有将其发布在邮件列表上 - 没有办法更新这些在线资源),如果您问我,打包版本是更好的选择grid.arrange
624赞 David LeBauer 10/15/2010 #4

任何并排的 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 中的函数值得一试,作为 的替代品。请参阅下面 @claus-wilke 的答案这个小插曲,了解等效的方法;但该功能允许根据这个小插图对绘图位置和大小进行更精细的控制。plot_gridgrid.arrange

评论

2赞 Jim 9/6/2013
当我使用 ggplot 对象运行您的代码时,sidebysideplot 为 null。如果要将输出保存到文件,请使用 gridArrange。请参阅 stackoverflow.com/questions/17059099/...
0赞 David LeBauer 9/7/2013
@Jim感谢您指出这一点。我已经修改了我的答案。如果还有任何问题,请告诉我。
1赞 Atticus29 6/14/2014
grid.aarange 现在被剥夺了吗?
0赞 blakeoft 10/2/2014
?grid.arrange让我觉得这个函数现在叫arrangeGrob。我能够通过做然后做我想做的事.a <- arrangeGrob(p1, p2)print(a)
0赞 David LeBauer 10/2/2014
你看过这些例子@blakeoft吗? 仍然是一个有效的、未弃用的函数。您是否尝试过使用该功能?如果不是你所期望的,会发生什么。grid.arrange
52赞 David LeBauer 12/6/2011 #5

您可以使用 Winston Chang 的 R 说明书中的以下函数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 ))
    }

}
258赞 Claus Wilke 7/5/2015 #6

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

我编写了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

返回的对象是另一个 ggplot2 对象,您可以像往常一样保存它:plot_grid()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 = 2save_plot()save_plot()

有关如何在网格中排列绘图的更深入描述,请参阅此小插图。还有一个小插曲解释了如何用一个共同的传说来制作情节。

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

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

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

theme_set(theme_gray())

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

## 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

更新:

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

评论

0赞 VAR121 3/18/2016
在输出 cowplot 中,删除了两个图的背景主题?还有其他选择吗?
0赞 Claus Wilke 3/19/2016
@VAR121 是的,这是一行代码。在介绍性小插曲第一部分的末尾解释:cran.rstudio.com/web/packages/cowplot/vignettes/......
1赞 Herman Toothrot 9/21/2017
是否可以使用此软件包对所有地块具有相同的 y 比例?
1赞 Seanosapien 11/14/2017
不过,您可以在使用 grid.arrange() 之前为每个图设置一个 ggtitle() 吗?
2赞 David 1/5/2022
有没有办法使用指定面板尺寸?我只是试图使用它,它压扁了我的数字。也许我需要在使用前指定 ggplot 图像大小?plot_grid()plot_grid()
9赞 shiny 1/30/2017 #7

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

1赞 IVIM 4/20/2017 #8

如果您想使用循环绘制多个 ggplot 图(例如,如此处所述:使用循环在 ggplot 中创建具有不同 Y 轴值的多个图),则上述解决方案可能效率不高,这是分析未知(或大型)数据集所需的步骤(例如,当您想绘制数据集中所有变量的计数时)。

下面的代码显示了如何使用上面提到的“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))ggplotaes_string(strX)aesaes_stringggplot

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

评论

1赞 Tung 4/3/2019
请注意,您的代码会在其中增长对象,这些对象效率非常低,因此不建议在 中。请参阅这些很棒的帖子,以找到更好的方法:R中的高效累积在数据框的行上应用函数R中面向行的工作流,使用tidyverseplotsfor-loopR
0赞 Tung 4/3/2019
循环遍历变量的更有效方法是使用自 stackoverflow.com/a/52045613/786542 年以来一直可用的方法tidy evaluationggplot2 v.3.0.0
-2赞 tim 8/29/2017 #9

该软件包为您提供了一种很好的方法来做到这一点,以适合发布的方式。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

评论

3赞 David LeBauer 5/26/2018
另请参阅上面软件包作者的更详细的答案和理由 stackoverflow.com/a/31223588/199217
15赞 baptiste 12/2/2017 #10

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

我写是为了提供一个尽可能接近的简单界面。在最简单的形式中,代码如下所示: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

此小插图中详细介绍了更多选项。

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

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

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

这两个函数都与 兼容。有关不同选项的一般概述以及一些历史背景,此小插图提供了其他信息ggsave()

90赞 Deena 1/23/2018 #11

使用 patchwork 包,您可以简单地使用 operator:+

library(ggplot2)
library(patchwork)

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


p1 + p2

patchwork

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

评论

2赞 Magnus 10/30/2020
Patchwork 是图形布局包领域的一个很棒的补充。谢谢!
1赞 Paul 'Joey' McMurdie 11/2/2021
多年来,我使用过大多数其他解决方案,我不得不说这个答案(拼凑)是迄今为止我的首选。
17赞 Tung 7/7/2018 #12

还有值得一提的多面板图形包。另请参阅此答案

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 软件包 (v0.2.0.9000) 于 2018-07-06 创建。

1赞 Mayank Agrawal 5/12/2020 #13

根据我的经验,如果您尝试在循环中生成绘图,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 类别的所有级别生成多个图。

示例图像

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

评论

0赞 Peter 5/12/2020
你的答案如何改进洗礼者在17年12月2日4:20的回答?你的答案似乎是重复的。在这里查看什么是可接受的答案 如何回答
0赞 Mayank Agrawal 5/13/2020
我无法根据需要在一个循环中划分情节,因此提出了建议。最初,我编写了我的 for 循环的完整片段及其实现,但后来决定暂时不这样做。将在一周左右更新完整代码。
0赞 Mayank Agrawal 5/14/2020
我首先尝试使用 cowplot 包本身来做到这一点,但没有成功。在我的快速扫描中,没有人提到 for 循环中的多个绘图解决方案,因此我的评论。如果我错了,请向我提出任何评论。
1赞 Peter 5/14/2020
如果答案中的代码包含 for 循环,则情况会有所不同。
0赞 Mayank Agrawal 5/14/2020
我会在一周内更新它,这里和 Kaggle 上的整个项目。干杯。
3赞 Niels Holst 2/15/2022 #14

还要从包装中考虑。它有很多好处,包括在绘图之间对齐轴以及将常见图例合并为一个图例的选项。ggarrangeggpubr