关于r:为组合ggplots添加通用图例

Add a common Legend for combined ggplots

我有两个ggplots,它们与grid.arrange水平对齐。 我浏览了很多论坛帖子,但是我尝试的所有内容似乎都是现在已更新并命名为其他名称的命令。

我的数据看起来像这样;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 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")))

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


您也可以从ggpubr包中使用ggarrange并设置" common.legend = TRUE":

1
2
3
4
5
6
7
8
9
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


2015年2月更新

请参阅下面的史蒂文答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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


罗兰的答案需要更新。参见:https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs

此方法已针对ggplot2 v1.0.0更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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。这个例子比上面的解决方案更复杂,但是仍然为我解决了。


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

1
2
3
4
5
6
7
8
9
10
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")

>
</p>
<p>
由reprex软件包(v0.2.1)创建于2019-12-13
</p>
<div class=


我建议使用Cowplot。从他们的R小插图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 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


@Giuseppe,您可能需要考虑这一点,以灵活地指定地块布置(从此处进行修改):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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控制布置图的布局:

1
2
3
4
5
6
7
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


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

例如:

1
2
3
4
5
6
7
8
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数据集的另一个示例。这表明,如果您的绘图之间只有一个公用变量,您甚至可以使它起作用。

1
2
3
4
5
6
7
8
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

第二个示例唯一棘手的事情是,当您将所有内容组合到一个数据框中时,因子变量将被强制转换为数值。因此,理想情况下,主要是在所有感兴趣的变量都属于同一类型时才执行此操作。


如果两个图例的图例都相同,则有一个使用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


@吉塞佩:

我完全不知道Grobs等问题,但是我为两个图表共同制定了一个解决方案,应该可以扩展到任意数,但不能扩展为性感函数:

1
2
3
4
5
6
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))