为组合的 ggplot 添加一个公共图例

我有两个与 grid.arrange水平对齐的 ggplot。我浏览了很多论坛帖子,但是我尝试的所有东西似乎都是命令,现在这些命令被更新并命名为其他东西。

我的数据是这样的:

# Data plot 1
axis1     axis2
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.417117 -0.002592
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.186860 -0.203273


# Data plot 2
axis1     axis2
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988


#And I run this:
library(ggplot2)
library(gridExtra)




groups=c('group1','group2','group3','group4','group1','group2','group3','group4')


x1=data1[,1]
y1=data1[,2]


x2=data2[,1]
y2=data2[,2]


p1=ggplot(data1, aes(x=x1, y=y1,colour=groups)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)


p2=ggplot(data2, aes(x=x2, y=y2,colour=groups)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)


#Combine plots
p3=grid.arrange(
p1 + theme(legend.position="none"), p2+ theme(legend.position="none"), nrow=1, widths = unit(c(10.,10), "cm"), heights = unit(rep(8, 1), "cm")))

我如何从这些图中提取图例并将其添加到合并图的底部/中心?

207969 次浏览

最新情况2021年3月

这个答案仍然有一些价值,但主要是历史价值。多年来,自从这张帖子发布以来,更好的解决方案已经可以通过软件包获得。你应该考虑下面更新的答案。

2015年最新情况

参见 下面是史蒂文的回答


df1 <- read.table(text="group   x     y
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.417117 -0.002592
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.186860 -0.203273",header=TRUE)


df2 <- read.table(text="group   x     y
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988",header=TRUE)




library(ggplot2)
library(gridExtra)


p1 <- ggplot(df1, aes(x=x, y=y,colour=group)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8) + theme(legend.position="bottom")


p2 <- ggplot(df2, aes(x=x, y=y,colour=group)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)


#extract legend
#https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
g_legend<-function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}


mylegend<-g_legend(p1)


p3 <- grid.arrange(arrangeGrob(p1 + theme(legend.position="none"),
p2 + theme(legend.position="none"),
nrow=1),
mylegend, nrow=2,heights=c(10, 1))

以下是由此产生的情节: 2 plots with common legend

罗兰的答案需要更新

这个方法已经针对 ggplot2 v1.0.0进行了更新。

library(ggplot2)
library(gridExtra)
library(grid)




grid_arrange_shared_legend <- function(...) {
plots <- list(...)
g <- ggplotGrob(plots[[1]] + theme(legend.position="bottom"))$grobs
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
lheight <- sum(legend$height)
grid.arrange(
do.call(arrangeGrob, lapply(plots, function(x)
x + theme(legend.position="none"))),
legend,
ncol = 1,
heights = unit.c(unit(1, "npc") - lheight, lheight))
}


dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data=dsamp, colour=clarity)
p2 <- qplot(cut, price, data=dsamp, colour=clarity)
p3 <- qplot(color, price, data=dsamp, colour=clarity)
p4 <- qplot(depth, price, data=dsamp, colour=clarity)
grid_arrange_shared_legend(p1, p2, p3, p4)

注意缺少 ggplot_gtableggplot_build。取而代之的是 ggplotGrob。这个例子比上面的解决方案稍微复杂一些,但它仍然解决了我的问题。

@ 朱塞佩:

我完全不知道 Grobs 之类的东西,但是我拼凑出了两个情节的解决方案,应该可以扩展到任意数字,但是它不是一个性感的函数:

plots <- list(p1, p2)
g <- ggplotGrob(plots[[1]] + theme(legend.position="bottom"))$grobs
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
lheight <- sum(legend$height)
tmp <- arrangeGrob(p1 + theme(legend.position = "none"), p2 + theme(legend.position = "none"), layout_matrix = matrix(c(1, 2), nrow = 1))
grid.arrange(tmp, legend, ncol = 1, heights = unit.c(unit(1, "npc") - lheight, lheight))

@ Giuseppe,你可能需要考虑一个灵活的地块安排规格(从 给你修改) :

library(ggplot2)
library(gridExtra)
library(grid)


grid_arrange_shared_legend <- function(..., nrow = 1, ncol = length(list(...)), position = c("bottom", "right")) {


plots <- list(...)
position <- match.arg(position)
g <- ggplotGrob(plots[[1]] + theme(legend.position = position))$grobs
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
lheight <- sum(legend$height)
lwidth <- sum(legend$width)
gl <- lapply(plots, function(x) x + theme(legend.position = "none"))
gl <- c(gl, nrow = nrow, ncol = ncol)


combined <- switch(position,
"bottom" = arrangeGrob(do.call(arrangeGrob, gl),
legend,
ncol = 1,
heights = unit.c(unit(1, "npc") - lheight, lheight)),
"right" = arrangeGrob(do.call(arrangeGrob, gl),
legend,
ncol = 2,
widths = unit.c(unit(1, "npc") - lwidth, lwidth)))
grid.newpage()
grid.draw(combined)


}

额外的参数 nrowncol控制排列好的地块的布局:

dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data = dsamp, colour = clarity)
p2 <- qplot(cut, price, data = dsamp, colour = clarity)
p3 <- qplot(color, price, data = dsamp, colour = clarity)
p4 <- qplot(depth, price, data = dsamp, colour = clarity)
grid_arrange_shared_legend(p1, p2, p3, p4, nrow = 1, ncol = 4)
grid_arrange_shared_legend(p1, p2, p3, p4, nrow = 2, ncol = 2)

enter image description here enter image description here

我建议使用牛仔图。从他们的 R Vignette:

# load cowplot
library(cowplot)


# down-sampled diamonds data set
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]


# Make three plots.
# We set left and right margins to 0 to remove unnecessary spacing in the
# final plot arrangement.
p1 <- qplot(carat, price, data=dsamp, colour=clarity) +
theme(plot.margin = unit(c(6,0,6,0), "pt"))
p2 <- qplot(depth, price, data=dsamp, colour=clarity) +
theme(plot.margin = unit(c(6,0,6,0), "pt")) + ylab("")
p3 <- qplot(color, price, data=dsamp, colour=clarity) +
theme(plot.margin = unit(c(6,0,6,0), "pt")) + ylab("")


# arrange the three plots in a single row
prow <- plot_grid( p1 + theme(legend.position="none"),
p2 + theme(legend.position="none"),
p3 + theme(legend.position="none"),
align = 'vh',
labels = c("A", "B", "C"),
hjust = -1,
nrow = 1
)


# extract the legend from one of the plots
# (clearly the whole thing only makes sense if all plots
# have the same legend, so we can arbitrarily pick one.)
legend_b <- get_legend(p1 + theme(legend.position="bottom"))


# add the legend underneath the row we made earlier. Give it 10% of the height
# of one plot (via rel_heights).
p <- plot_grid( prow, legend_b, ncol = 1, rel_heights = c(1, .2))
p

combined plots with legend at bottom

您还可以使用 Ggpubr包中的 乱七八糟的并设置“ common.Legend = TRUE”:

library(ggpubr)


dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data = dsamp, colour = clarity)
p2 <- qplot(cut, price, data = dsamp, colour = clarity)
p3 <- qplot(color, price, data = dsamp, colour = clarity)
p4 <- qplot(depth, price, data = dsamp, colour = clarity)


ggarrange(p1, p2, p3, p4, ncol=2, nrow=2, common.legend = TRUE, legend="bottom")

enter image description here

如果在两个图中绘制相同的变量,最简单的方法是将数据帧合并为一个,然后使用 facet _ wrap。

举个例子:

big_df <- rbind(df1,df2)


big_df <- data.frame(big_df,Df = rep(c("df1","df2"),
times=c(nrow(df1),nrow(df2))))


ggplot(big_df,aes(x=x, y=y,colour=group))
+ geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)
+ facet_wrap(~Df)

Plot 1

另一个使用菱形数据集的例子。这表明,如果你的图之间只有一个公共变量,你甚至可以让它工作。

diamonds_reshaped <- data.frame(price = diamonds$price,
independent.variable = c(diamonds$carat,diamonds$cut,diamonds$color,diamonds$depth),
Clarity = rep(diamonds$clarity,times=4),
Variable.name = rep(c("Carat","Cut","Color","Depth"),each=nrow(diamonds)))


ggplot(diamonds_reshaped,aes(independent.variable,price,colour=Clarity)) +
geom_point(size=2) + facet_wrap(~Variable.name,scales="free_x") +
xlab("")

Plot 2

对于第二个示例,唯一需要注意的是,当您将所有内容组合到一个数据框架中时,factor 变量被强制为 numeric。所以理想情况下,主要是当所有感兴趣的变量都是相同类型时。

一个新的、有吸引力的解决方案是使用 patchwork。语法非常简单:

library(ggplot2)
library(patchwork)


p1 <- ggplot(df1, aes(x = x, y = y, colour = group)) +
geom_point(position = position_jitter(w = 0.04, h = 0.02), size = 1.8)
p2 <- ggplot(df2, aes(x = x, y = y, colour = group)) +
geom_point(position = position_jitter(w = 0.04, h = 0.02), size = 1.8)


combined <- p1 + p2 & theme(legend.position = "bottom")
combined + plot_layout(guides = "collect")

Reprex 软件包于2019-12-13年度创作(v0.2.1)

如果图例对于两个图都是相同的,那么有一个使用 grid.arrange的简单解决方案(假设您希望您的图例与两个图垂直或水平对齐)。只需保留最底部或最右侧的图例,而省略另一个图例。但是,只在一个情节中添加图例会改变一个情节相对于另一个情节的大小。为了避免这种情况,可以使用 heights命令手动调整并保持它们的大小不变。您甚至可以使用 grid.arrange来制作公共轴标题。请注意,这将需要除 library(gridExtra)之外的 library(grid)。对于垂直地块:

y_title <- expression(paste(italic("E. coli"), " (CFU/100mL)"))


grid.arrange(arrangeGrob(p1, theme(legend.position="none"), ncol=1),
arrangeGrob(p2, theme(legend.position="bottom"), ncol=1),
heights=c(1,1.2), left=textGrob(y_title, rot=90, gp=gpar(fontsize=20)))

下面是我正在做的一个项目的一个类似图表的结果: enter image description here