与ggplot2并排绘图

我想使用ggplot2包并排放置两个plot,即做等效的par(mfrow=c(1,2))

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

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

我需要把它们放到同一个数据帧里吗?

qplot(displ, hwy, data=mpg, facets = . ~ year) + geom_smooth()
603966 次浏览

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

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或重塑中有更好的技巧-我仍然没有真正跟上速度

使用重塑包可以完成如下操作。

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)

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


Stephen Turner Getting Genetics Done blog上发布了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
}
}
}

并排的任意ggplot(或网格上的n个plot)

gridExtra包中的函数grid.arrange()将组合多个plot;这就是把两个放在一起的方法。

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

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

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

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

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

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

这相当于使用par(mfrow = c(1,2))创建两个不同的plot。这不仅节省了整理数据的时间,而且当你想要两个不同的图时,这是必要的。


附录:facet的使用

切面有助于为不同的群体制作相似的图。下面的许多回答都指出了这一点,但我想用与上面的图等效的例子来强调这种方法。

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的替代。参见下面的回答 by @ clause -wilke和这个描述以获得等效的方法;但该函数允许基于这个描述对地块位置和大小进行更精细的控制。

你可以从温斯顿·张的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 ))
}


}

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

或者,你也可以使用cowplot函数save_plot(),它是ggsave()的一个薄包装,可以很容易地获得组合plot的正确尺寸,例如:

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

(ncol = 2参数告诉save_plot()有两个并排的图像,并且save_plot()使保存的图像宽度增加一倍。)

有关如何在网格中安排图的更深入的描述,请参阅这个描述。。还有一个小插图解释如何使用共享的传奇。创建图

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

我认为总是为每个情节指定一个特定的主题是一个很好的实践,就像我在上面的例子中对+ theme_bw()所做的那样。如果您指定了一个特定的主题,那么默认主题并不重要。

你可以用一行代码做到这一点:

theme_set(theme_gray())

你也可以不调用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

更新:

  • 从cowplot 1.0开始,默认的ggplot2主题不再更改。
  • 从ggplot2 3.0.0开始,plot可以直接被标记,参见在这里。

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

如果你想使用循环绘制多个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打印的所有变量的Counts

dt = ggplot2::diamonds
plotAllCounts(dt)
需要注意的一件事是:
在上面的代码中使用aes(get(strX)),而不是aes_string(strX),将不会绘制所需的图形,通常在使用ggplot时在循环中使用。相反,它会多次绘制最后一个图形。我还没有弄清楚为什么-它可能必须做aesaes_stringggplot中被调用

除此之外,希望你会发现这个函数有用。

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

Ggplot2基于网格图形,网格图形提供了在页面上安排图形的不同系统。par(mfrow...)命令没有一个直接的对等物,因为网格对象(称为grob)不一定是立即绘制的,但在转换为图形输出之前,可以作为常规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

更多选项在这个描述中详细描述。

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

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

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

这两个函数都与ggsave()兼容。对于不同选项的一般概述,以及一些历史上下文,这个小插图提供了额外的信息. c。

使用拼接而成包,你可以简单地使用+操作符:

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

其他操作符包括/,用于堆叠图,将图并排放置,()用于对元素进行分组。例如,你可以用(p1 | p2 | p3) /p配置3个地块的顶部行和一个地块的底部行。有关更多示例,请参见包的文档

还有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

< img src = " https://i.imgur.com/b3zFLMp.png " alt = " " >

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

< img src = " https://i.imgur.com/G3vmFaF.png " alt = " " >

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

< img src = " https://i.imgur.com/CS13Vdm.png " alt = " " >

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

< img src = " https://i.imgur.com/fagCwvv.png " alt = " " >

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

根据我的经验,网格。如果您试图在循环中生成情节,那么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循环中的一个Sample Plots包含在下面。 以上循环将为BMI类别的所有级别生成多个图

样本图像 .

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

再考虑ggpubr包中的ggarrange。它有很多好处,包括在情节之间对齐轴和将常见图例合并为一个图例的选项。