如何在ggplot中独立定位两个图例

时间:2012-10-30 16:50:37

标签: r ggplot2 legend

标题很好地涵盖了它。

我有两个与尺寸和颜色有关的传说,并且希望在图表的顶部和图片中有一个。

这是否可行,如果可行,

TIA

3 个答案:

答案 0 :(得分:29)

可以通过从图中提取单独的图例,然后在相关图中排列图例来完成。这里的代码使用gtable包中的函数进行提取,然后使用gridExtra包中的函数进行排列。目的是绘制包含颜色图例和尺寸图例的图表。首先,从仅包含颜色图例的图中提取颜色图例。其次,从仅包含尺寸图例的图表中提取尺寸图例。第三,绘制一个不包含图例的情节。第四,将情节和两个传说安排在一个新的情节中。

# Some data
df <- data.frame(
  x = 1:10,
  y = 1:10,
  colour = factor(sample(1:3, 10, replace = TRUE)),
  size = factor(sample(1:3, 10, replace = TRUE)))

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

    ### Step 1
# Draw a plot with the colour legend
(p1 <- ggplot(data = df, aes(x=x, y=y)) +
   geom_point(aes(colour = colour)) +
   theme_bw() +
   theme(legend.position = "top"))

# Extract the colour legend - leg1
leg1 <- gtable_filter(ggplot_gtable(ggplot_build(p1)), "guide-box") 

    ### Step 2
# Draw a plot with the size legend
(p2 <- ggplot(data = df, aes(x=x, y=y)) +
   geom_point(aes(size = size)) +
   theme_bw())

# Extract the size legend - leg2
leg2 <- gtable_filter(ggplot_gtable(ggplot_build(p2)), "guide-box") 

    # Step 3
# Draw a plot with no legends - plot
(plot <- ggplot(data = df, aes(x=x, y=y)) +
   geom_point(aes(size = size, colour = colour)) +
   theme_bw() +
   theme(legend.position = "none"))

    ### Step 4
# Arrange the three components (plot, leg1, leg2)
# The two legends are positioned outside the plot: 
# one at the top and the other to the side.
plotNew <- arrangeGrob(leg1, plot, 
         heights = unit.c(leg1$height, unit(1, "npc") - leg1$height), ncol = 1)

plotNew <- arrangeGrob(plotNew, leg2,
          widths = unit.c(unit(1, "npc") - leg2$width, leg2$width), nrow = 1)

grid.newpage()
grid.draw(plotNew)

# OR, arrange one legend at the top and the other inside the plot.
plotNew <- plot + 
        annotation_custom(grob = leg2, xmin = 7, xmax = 10, ymin = 0, ymax = 4)

plotNew <- arrangeGrob(leg1, plotNew,
     heights = unit.c(leg1$height, unit(1, "npc") -  leg1$height), ncol = 1)

grid.newpage()
grid.draw(plotNew)

enter image description here

enter image description here

答案 1 :(得分:7)

以下是使用ggplot2cowplot(= ggplot2扩展名)包的另一种解决方案。

该方法类似于Sandys,因为它将图例作为单独的对象取出,并允许您单独进行放置。它主要设计用于多个图例,这些图例属于图形网格中的两个或多个图。

使用herby的函数g_legend取自answer

这个想法如下:

  1. 创建Plot1,Plot2,...,PlotX 不带图例
  2. 使用图例创建Plot1,Plot2,...,PlotX
  3. 将第2步中的图例提取为单独的对象
  4. 设置图例网格并按照您想要的方式排列图例
  5. 创建合并图表和图例的网格
  6. 它似乎有点复杂,时间/代码消耗但是设置一次,你可以适应并用于各种情节/图例定制。

    library(ggplot2) 
    library(cowplot)
    
    # set up function  
    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) }
    
    # Some data 
    df <- data.frame(
        Name = factor(rep(c("A", "B", "C"), 12)),
        Month = factor(rep(1:12, each=3)),
        Temp = sample(0:40, 12),
        Precip = sample(50:400, 12))
    
    # create plot1 
    plot1 <- ggplot(df, aes(Month, Temp, fill = Name)) + 
        geom_point(show.legend = F, aes(group = Name, colour = Name), 
            size = 3, shape = 17) + 
        geom_smooth(method = "loess", se = F, 
             aes(group = Name, colour = Name), 
             show.legend = F, size = 0.5, linetype = "dashed")
    
    # create plot2 
    plot2 <- ggplot(df, aes(Month, Precip, fill = Name)) + 
        geom_bar(stat = "identity", position = "dodge", show.legend = F) +
        geom_smooth(method = "loess", se = F, 
            aes(group = Name, colour = Name), 
            show.legend = F, size = 1, linetype = "dashed") +
        scale_fill_grey()
    
    # create legend1 
    legend1 <- ggplot(df, aes(Month, Temp)) + 
        geom_point(show.legend = T, aes(group = Name, colour = Name), 
            size = 3, shape = 17) + 
        geom_smooth(method = "loess", se = F,aes(group = Name, colour = Name), 
             show.legend = T, size = 0.5, linetype = "dashed") +
        labs(colour = "Station") +
        theme(legend.text=element_text(size=8),
              legend.title = element_text(face = "italic", 
                   angle = -0, size = 10))
    
    # create legend2 
    legend2 <- ggplot(df, aes(Month, Precip, fill = Name)) + 
        geom_bar(stat = "identity", position = "dodge", show.legend = T) +
        scale_fill_grey() +
        guides(fill = 
             guide_legend(title = "",
                  title.theme = element_text(face = "italic",
                       angle = -0, size = 10))) +
        theme(legend.text=element_text(size=8))
    
    
    # extract "legends only" from ggplot object
    legend1 <- g_legend(legend1) 
    legend2 <- g_legend(legend2)
    
    # setup legends grid 
    legend1_grid <- cowplot::plot_grid(legend1, align = "v", nrow = 2)
    
    # add second legend to grid, specifying its location 
    legends <- legend1_grid + 
         ggplot2::annotation_custom(grob = legend2, 
              xmin = 0.5, xmax = 0.5, ymin = 0.55, ymax = 0.55)
    
    # plot "plots" + "legends" (with legends in between plots) 
    cowplot::plot_grid(plot1, legends, plot2, ncol = 3, 
         rel_widths = c(0.45, 0.1, 0.45))
    

    <强>示例:

    Example http://i65.tinypic.com/jl1lef.png

    更改最终plot_grid()调用的顺序会将图例移到右侧:

    cowplot::plot_grid(plot1, plot2, legends, ncol = 3, 
                       rel_widths = c(0.45, 0.45, 0.1))
    

    Example2 http://i68.tinypic.com/314yn9i.png

答案 2 :(得分:4)

根据我的理解,ggplot2基本上对传说的控制非常有限。这是Hadley的书(第111页)中的一段:

ggplot2尝试使用尽可能少的图例来准确传达绘图中使用的美学。如果变量与多个美学一起使用,它可以通过组合图例来实现。图6.14显示了点geom的一个示例:如果颜色和形状都映射到同一个变量,则只需要一个图例。为了合并图例,它们必须具有相同的名称(相同的图例标题)。因此,如果您更改其中一个合并图例的名称,则需要针对所有这些图例进行更改。