在具有不同输入的多个ggplot下添加公共图例

时间:2018-02-01 13:04:12

标签: r plot ggplot2 legend

我有一个数据集的图,在每个图中都有不同的过滤方式。我的目标是获得一个共同的图例,其中包括图表my_hist_z,my_hist_y,my_hist_x上显示的所有因素。

示例:

library(ggplot2); library(gridExtra); library(grid)
z <- subset(diamonds, subset = cut == "Fair" | cut == "Good")
y <- subset(diamonds, subset = cut == "Good")
x <- subset(diamonds, subset = cut == "Premium" | cut == "Fair")

colours = c("Fair" =  "#666362", 
        "Good" = "#D40511", 
        "Very Good" = "#FFCC00", 
        "Premium" = "#000000",  
        "Ideal" = "#BBBBB3") 

my_hist_z <-ggplot(z, aes(clarity, fill=cut)) + geom_bar() + 
scale_fill_manual(values = colours)
my_hist_y <-ggplot(y, aes(clarity, fill=cut)) + geom_bar() + 
scale_fill_manual(values = colours)
my_hist_x <-ggplot(x, aes(clarity, fill=cut)) + geom_bar() + 
scale_fill_manual(values = colours)

my_hist<-ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar() + 
scale_fill_manual(values = colours)

潜在解决方案: https://andyphilips.github.io/blog/2017/04/04/single-legend-for-multiple-plots.html 仅使用已定义绘图的一个图例,但不会聚合所有涉及的元素。

有什么想法吗?理想情况下,解决方案是动态的,因为这些图被描述为for循环的一部分。 谢谢!

2 个答案:

答案 0 :(得分:2)

首先,删除三个图例中除了一个之外的所有图例,并设置段drop = FALSE

library(ggplot2)
library(gridExtra)
library(grid)
z <- subset(diamonds, subset = cut == "Fair" | cut == "Good")
y <- subset(diamonds, subset = cut == "Good")
x <- subset(diamonds, subset = cut == "Premium" | cut == "Fair")

colours = c("Fair" =  "#666362", 
  "Good" = "#D40511", 
  "Very Good" = "#FFCC00", 
  "Premium" = "#000000",  
  "Ideal" = "#BBBBB3") 

my_hist_z <-ggplot(z, aes(clarity, fill=cut)) + geom_bar() + 
  scale_fill_manual(values = colours, drop = FALSE) + 
  theme(legend.position = "none")
my_hist_y <-ggplot(y, aes(clarity, fill=cut)) + geom_bar() + 
  scale_fill_manual(values = colours, drop = FALSE) + 
  theme(legend.position = "none")
my_hist_x <-ggplot(x, aes(clarity, fill=cut)) + geom_bar() + 
  scale_fill_manual(values = colours, drop = FALSE)

其次,使用上面提到的post中的函数grid_arrange_shared_legend()来合并所有三个图

grid_arrange_shared_legend <- function(..., ncol = length(list(...)), nrow = 1, 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, ncol = ncol, nrow = nrow)

  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)

  # return gtable invisibly
  invisible(combined)
}

grid_arrange_shared_legend(my_hist_z,my_hist_y,my_hist_x, ncol = 3)

enter image description here

答案 1 :(得分:1)

这样可行,但不是我要说的最优雅的解决方案:

library(ggplot2)
z <- subset(diamonds, subset = cut == "Fair" | cut == "Good")
y <- subset(diamonds, subset = cut == "Good")
x <- subset(diamonds, subset = cut == "Premium" | cut == "Fair")

colours = c("Fair" =  "#666362", 
            "Good" = "#D40511", 
            "Very Good" = "#FFCC00", 
            "Premium" = "#000000",  
            "Ideal" = "#BBBBB3") 

all_levels <- unique(c(levels(factor(x$cut)), levels(factor(y$cut)), levels(factor(z$cut))))

x$cut <- factor(x$cut, levels = all_levels)
y$cut <- factor(y$cut, levels = all_levels)
z$cut <- factor(z$cut, levels = all_levels)

ggplot(z, aes(clarity, fill = cut)) + geom_bar() +
  scale_fill_manual(values = colours, drop = F)

enter image description here

修改

如果您的所有地块都属于同一类型,则可将它们合并,然后使用facet_wrap进行绘制:

z$subset <- "z"
y$subset <- "y"
x$subset <- "x"

xyz <- rbind(x, y, z)

ggplot(xyz, aes(clarity, fill = cut)) + geom_bar() +
  facet_wrap(~subset) +
  scale_fill_manual(values = colours) +
  theme(legend.position = "bottom")

enter image description here

您可以通过将strip.text = element_blank()添加到theme来隐藏小平面标签。

如果您的情节类型不同,我担心您不得不使用杨的解决方案。