ggplot2的并排图


338

我想使用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是硬性要求吗?
JD Long

8
不。但是我已经花时间调整了qplots,所以这就是我喜欢的方式。:-)我正在尝试ggplot。
Christopher DuBois,2009年


1
要获得一个不错的概览,请参见鸡蛋包装的插图:在页面上布置多个地块
Henrik

Answers:


503

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

该功能grid.arrange()gridExtra包将结合多条曲线; 这就是您将两个并排放置的方式。

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)

更新资料

plot_grid在函数cowplot是值得检查作为替代grid.arrange。请参见下面@ claus-wilke 的答案此插图,以了解等效的方法;但此功能可根据此小插图更好地控制绘图位置和大小。


2
当我使用ggplot对象运行您的代码时,sidebysideplot为null。如果要将输出保存到文件,请使用gridArrange。参见stackoverflow.com/questions/17059099/…– 2013
吉姆

@Jim感谢您指出这一点。我已经修改了答案。让我知道是否还有任何疑问。
David LeBauer 2013年

1
现在grid.aarange了吗?
Atticus14年

?grid.arrange让我认为该函数现在称为rangingGrob。我能够通过做a <- arrangeGrob(p1, p2)然后做自己想做的事情print(a)
blakeoft 2014年

您是否看过@blakeoft的示例?grid.arrange仍然是有效的,不建议使用的函数。您是否尝试过使用该功能?如果不是您所期望的,会发生什么。
David LeBauer 2014年

159

基于该解决方案的一个缺点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()并使保存的图像宽两倍。)

有关如何在网格中布置图的更深入描述,请参见此插图。还有一个小插图,说明如何使用共享的图例进行绘制

一个经常引起混淆的地方是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开始,可以直接标记图,请参见此处。

在输出Cowplot中正在删除两个图的背景主题?还有其他选择吗?
VAR121 '16

@ VAR121是的,这是一行代码。解释在介绍小插曲,首节结束:cran.rstudio.com/web/packages/cowplot/vignettes/...
克劳斯维尔克

使用此软件包,所有样地的y比例尺是否可能相同?
Herman Toothrot

您必须手动设置y比例以匹配。或考虑刻面。
克劳斯威尔克

您可以在使用grid.arrange()之前为每个图设置一个ggtitle()吗?
Seanosapien

49

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

}

24

使用补丁包,您可以简单地使用+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

拼凑而成


仅出于完整性考虑,现在CRAN上也使用了修补程序。希望您对我的小编感到满意
Tjebo,

18

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

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的所有这些强大功能。


16

使用重塑包装,您可以执行以下操作。

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)

12

还有值得一提的multipanelfigure程序包。另请参阅此答案

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创建。


9

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)

在此处输入图片说明

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

一个普遍的抱怨是,例如,当它们具有不同大小的轴标签时,这些图不一定是对齐的,但这是设计grid.arrange使然:不尝试对ggplot2对象进行特殊处理,并将它们与其他grob一样对待(例如,格子图) )。它只是将杂物以矩形布局放置。

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

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

两种功能均与兼容ggsave()。对于不同选项的总体概述以及一些历史背景,此插图提供了其他信息


9

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

9
它基本上是一个非常过时的版本grid.arrange(希望我没有张贴在邮件列表的时候-有没有更新这些网上资源的方式),包装的版本是一个更好的选择,如果你问我
巴蒂斯特

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

在此处输入图片说明


1

如果您想使用循环绘制多个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)),通常会在循环中使用ggplot,而不是aes_string(strX)不会绘制所需的图形。相反,它将多次绘制最后一个绘图。我还没有弄清楚为什么-它可能必须执行aesaes_string被调用ggplot

否则,希望对您有用。


1
请注意,您的代码会增长效率极低的plots对象,for-loop因此不建议使用R。请查看这些精彩的帖子,以找到更好的方法:R中的有效累积使用tidyverse在R
Tung

遍历变量的更有效方法是使用stackoverflow.com/a/52045613/786542tidy evaluationggplot2 v.3.0.0
Tung

0

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

简短代码段:

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

与巴蒂斯特17年12月2日4:20的答案相比,您的答案有何改善?您的答案似乎是重复的。在这里查看什么是可以接受的答案。如何回答
Peter

我无法根据需要在循环中划分情节,因此无法提出建议。最初,我用它的实现编写了for循环的完整代码段,但是暂时不赞成这样做。将在一周左右的时间内更新完整代码。
Mayank Agrawal

检查大卫·勒鲍尔(David LeBauer)接受的答案
彼得

我最初尝试使用cowplot包来完成此操作,但未成功。在我的快速扫描中,没有人提到for循环内的多个绘图解决方案,因此也没有提及我的评论。如果我错了,请给我任何评论。
Mayank Agrawal

如果答案中的代码包含for循环,则该循环将有所不同。
彼得

-3

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

在此处输入图片说明


3
又见包作者的更详细的答案,上述理由stackoverflow.com/a/31223588/199217
大卫LeBauer
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.