【问题标题】:Create two legends for one ggplot2 graph and modify them为一个 ggplot2 图创建两个图例并修改它们
【发布时间】:2017-10-11 08:55:48
【问题描述】:

我在修改 ggplot2 图表中的图例时遇到了一些麻烦。具体来说,我想创建两个图例而不是一个,并更改其中的名称。

我想绘制三条曲线以及两条从 x 轴到第一条曲线的垂直线。我使用的代码如下:

# generate data

X1 <- as.data.frame(matrix(nrow = 10, ncol = 2))
colnames(X1) <- c("Weight", "Height")
X1$Weight <- seq(1:10)
X1$Height <- c(2, 3, 3.5, 4, 3.8, 5, 6.1, 5.4, 7, 7.1)

X2 <- as.data.frame(matrix(nrow = 10, ncol = 2))
colnames(X2) <- c("Weight", "Height")
X2$Weight <- c(seq(1:8), NA, NA)
X2$Height <- c(4, 3.4, 3.1, 6, 5.4, 6, 6.3, 7.5, NA, NA)

X3 <- as.data.frame(matrix(nrow = 10, ncol = 2))
colnames(X3) <- c("Weight", "Height")
X3$Weight <- c(seq(1:10))
X3$Height <- c(7, 6.9, 6.8, 6.1, 7, 7.5, 7.8, 9, 9.2, 9.1)

# create plot

require(ggplot2)
mycurve <- qplot(Weight, Height, data = X1, geom = "line", color = "X1",  
             main = "Plot", xlab = "Weight [kg]", ylab = "Height [m]", 
             xlim = range(X1$Weight))

mycurve + 
  geom_line(aes(X2$Weight, X2$Height, color = "X2"), 
            linetype = "twodash", na.rm = TRUE) +
  geom_line(aes(X3$Weight, X3$Height, color = "X3"), 
            linetype = "dotted", na.rm = TRUE) +
  geom_segment(aes(x = 5, y = 0, color = "Y1", xend = 5, yend = X1["5", 2]), 
               linetype="longdash") + 
  geom_segment(aes(x = 7, y = 0, color = "Y2", xend = 7, yend = X1["7", 2]), 
               linetype="longdash") + 
  scale_color_manual(values=c("X1" = "black", "X2" = "darkseagreen4", 
                              "X3" = "darkred", "Y1"="green2", "Y2"="blue")) +
  theme_bw()

它给了我这个输出:

现在我想更改图例。我要两个传说:

  • 一个标题为“Object”并给我“Object1”(X1)、“Object2”(X2) 和“Object3”(X3)
  • 另一个标题为“Lines”,包含“Line1”(Y1)和“Line2”(Y2)

此外,我希望“对象”具有以下顺序:“对象 2”、“对象 1”、“对象 3”。

如果能帮助您解决这个问题,我将不胜感激!

【问题讨论】:

    标签: r plot ggplot2


    【解决方案1】:

    我建议将您的数据合并到一个数据框中。传递给ggplot()更简洁:

    # combine data
    df <- rbind(X1, X2, X3)
    df$Group <- rep(c("Object1", "Object2", "Object3"), each = 10) 
    
    df <- rbind(df,
                data.frame(Weight = 5,
                           Height = c(0, X1["5", 2]),
                           Group = "Line1"),
                data.frame(Weight = 7,
                           Height = c(0, X1["7", 2]),
                           Group = "Line2"))
    

    在 ggplot 中,我们为每种比例类型设计了一个图例,因此拥有两个线条颜色图例并不是一件自然而然的事情。帖子here 讨论了一些方法。我使用了2nd solution

    # add legend groupings as unused factor levels
    # also specify legend order
    df$Group <- factor(df$Group, levels = c("Object",
                                            "Object2", "Object1", "Object3",
                                            " ",
                                            "Lines",
                                            "Line1", "Line2"))
    

    另外,我建议使用ggplot 而不是qplot。正如包的文档所指出的,qplot 被设计为一个方便的包装器,以与基本的plot 函数的语法保持一致,但ggplot 更擅长处理更复杂的绘图要求:

    p <- ggplot(df,
           aes(x = Weight, y = Height, 
               group = Group, linetype = Group, color = Group)) +
      geom_line() +
      scale_linetype_manual(values = c( # actual line types used in the plot
                                       "Object1" = "solid", 
                                       "Object2" = "twodash", 
                                       "Object3" = "dotted",
                                       "Line1" = "longdash", 
                                       "Line2" = "longdash",
                                        # placeholder values for legend titles
                                       "Object" = "solid", "Lines" = "solid", " " = "solid"),
                            drop = F) +
      scale_color_manual(values = c( # actual line types used in the plot
                                    "Object1" = "black", 
                                    "Object2" = "darkseagreen4", 
                                    "Object3" = "darkred",
                                    "Line1" = "green2", 
                                    "Line2" = "blue",
                                    # placeholder values for legend titles
                                    "Object" = "white", "Lines" = "white", " " = "white"),
                         drop = F) +
      labs(title = "Plot", x = "Weight [kg]", y = "Height [m]") +
      theme_bw() +
      theme(legend.title = element_blank())
    
    p
    

    编辑以包括更改单个图例标签:

    我们可以对单个图例标签进行进一步更改,以使伪图例标题与其他“正常”标签更加不同。由于 ggplot 的图例不是为处理这种用例而设计的,我们可以通过将绘图(ggplot2 对象)转换为 grob 对象(本质上是图形对象的嵌套列表)来破解它,并在那里进行修改:

    # convert original plot (saved as p) into a grob
    g <- ggplotGrob(p)
    

    找到与图例标签相对应的嵌套 grob(有一些方法可以使用代码按关键字搜索,但对于一次性用例,我发现查看列表更容易和更清晰......) :

    > g # grob 15 (named guide-box) contains the legend 
    TableGrob (10 x 9) "layout": 18 grobs
        z         cells       name                                   grob
    1   0 ( 1-10, 1- 9) background        rect[plot.background..rect.174]
    2   5 ( 5- 5, 3- 3)     spacer                         zeroGrob[NULL]
    3   7 ( 6- 6, 3- 3)     axis-l    absoluteGrob[GRID.absoluteGrob.124]
    4   3 ( 7- 7, 3- 3)     spacer                         zeroGrob[NULL]
    5   6 ( 5- 5, 4- 4)     axis-t                         zeroGrob[NULL]
    6   1 ( 6- 6, 4- 4)      panel               gTree[panel-1.gTree.104]
    7   9 ( 7- 7, 4- 4)     axis-b    absoluteGrob[GRID.absoluteGrob.117]
    8   4 ( 5- 5, 5- 5)     spacer                         zeroGrob[NULL]
    9   8 ( 6- 6, 5- 5)     axis-r                         zeroGrob[NULL]
    10  2 ( 7- 7, 5- 5)     spacer                         zeroGrob[NULL]
    11 10 ( 4- 4, 4- 4)     xlab-t                         zeroGrob[NULL]
    12 11 ( 8- 8, 4- 4)     xlab-b titleGrob[axis.title.x..titleGrob.107]
    13 12 ( 6- 6, 2- 2)     ylab-l titleGrob[axis.title.y..titleGrob.110]
    14 13 ( 6- 6, 6- 6)     ylab-r                         zeroGrob[NULL]
    15 14 ( 6- 6, 8- 8)  guide-box                      gtable[guide-box]
    16 15 ( 3- 3, 4- 4)   subtitle  zeroGrob[plot.subtitle..zeroGrob.171]
    17 16 ( 2- 2, 4- 4)      title   titleGrob[plot.title..titleGrob.170]
    18 17 ( 9- 9, 4- 4)    caption   zeroGrob[plot.caption..zeroGrob.172]
    
    > g$grobs[[15]] # grob 1 (named guides) contains the actual legend table
    TableGrob (5 x 5) "guide-box": 2 grobs
                                        z     cells                  name           grob
    99_ff1a4629bd4c693e1303e4eecfb18bd2 1 (3-3,3-3)                guides gtable[layout]
                                        0 (2-4,2-4) legend.box.background zeroGrob[NULL]
    
    > g$grobs[[15]]$grobs[[1]] # grobs 19-25 contain the legend labels
    TableGrob (12 x 6) "layout": 26 grobs
        z         cells        name                               grob
    1   1 ( 1-12, 1- 6)  background  rect[legend.background..rect.167]
    2   2 ( 2- 2, 2- 5)       title zeroGrob[guide.title.zeroGrob.125]
    3   3 ( 4- 4, 2- 2)  key-3-1-bg         rect[legend.key..rect.143]
    4   4 ( 4- 4, 2- 2)   key-3-1-1        segments[GRID.segments.144]
    5   5 ( 5- 5, 2- 2)  key-4-1-bg         rect[legend.key..rect.146]
    6   6 ( 5- 5, 2- 2)   key-4-1-1        segments[GRID.segments.147]
    7   7 ( 6- 6, 2- 2)  key-5-1-bg         rect[legend.key..rect.149]
    8   8 ( 6- 6, 2- 2)   key-5-1-1        segments[GRID.segments.150]
    9   9 ( 7- 7, 2- 2)  key-6-1-bg         rect[legend.key..rect.152]
    10 10 ( 7- 7, 2- 2)   key-6-1-1        segments[GRID.segments.153]
    11 11 ( 8- 8, 2- 2)  key-7-1-bg         rect[legend.key..rect.155]
    12 12 ( 8- 8, 2- 2)   key-7-1-1        segments[GRID.segments.156]
    13 13 ( 9- 9, 2- 2)  key-8-1-bg         rect[legend.key..rect.158]
    14 14 ( 9- 9, 2- 2)   key-8-1-1        segments[GRID.segments.159]
    15 15 (10-10, 2- 2)  key-9-1-bg         rect[legend.key..rect.161]
    16 16 (10-10, 2- 2)   key-9-1-1        segments[GRID.segments.162]
    17 17 (11-11, 2- 2) key-10-1-bg         rect[legend.key..rect.164]
    18 18 (11-11, 2- 2)  key-10-1-1        segments[GRID.segments.165]
    19 19 ( 4- 4, 4- 4)   label-3-3         text[guide.label.text.127]
    20 20 ( 5- 5, 4- 4)   label-4-3         text[guide.label.text.129]
    21 21 ( 6- 6, 4- 4)   label-5-3         text[guide.label.text.131]
    22 22 ( 7- 7, 4- 4)   label-6-3         text[guide.label.text.133]
    23 23 ( 8- 8, 4- 4)   label-7-3         text[guide.label.text.135]
    24 24 ( 9- 9, 4- 4)   label-8-3         text[guide.label.text.137]
    25 25 (10-10, 4- 4)   label-9-3         text[guide.label.text.139]
    26 26 (11-11, 4- 4)  label-10-3         text[guide.label.text.141]
    

    因此,我们可以找到对应于“对象”和“线条”的 grobs。它们是:

    g$grobs[[15]]$grobs[[1]]$grobs[[19]] # label for "Object"
    g$grobs[[15]]$grobs[[1]]$grobs[[24]] # label for "Lines"
    
    > str(g$grobs[[15]]$grobs[[1]]$grobs[[19]]) # examine a label
    List of 11
     $ label        : chr "Object"
     $ x            :Class 'unit'  atomic [1:1] 0
      .. ..- attr(*, "valid.unit")= int 0
      .. ..- attr(*, "unit")= chr "npc"
     $ y            :Class 'unit'  atomic [1:1] 0.5
      .. ..- attr(*, "valid.unit")= int 0
      .. ..- attr(*, "unit")= chr "npc"
     $ just         : chr "centre"
     $ hjust        : num 0
     $ vjust        : num 0.5
     $ rot          : num 0
     $ check.overlap: logi FALSE
     $ name         : chr "guide.label.text.214"
     $ gp           :List of 5
      ..$ fontsize  : num 8.8
      ..$ col       : chr "black"
      ..$ fontfamily: chr ""
      ..$ lineheight: num 0.9
      ..$ font      : Named int 1
      .. ..- attr(*, "names")= chr "plain"
      ..- attr(*, "class")= chr "gpar"
     $ vp           : NULL
     - attr(*, "class")= chr [1:3] "text" "grob" "gDesc"
    

    我们可以看到在.$gp 下捕获了格式(图形参数列表,请参阅here 了解更多信息)。我们可以列出更改,并在每个标签的原始列表中替换它们:

    # make changes to format (examples of various things that can be changed)
    gp.new <- list(fontsize = 10, # increase font size
                   col = "red",   # change font color
                   font = 2L)     # change from plain (1L) to bold (2L) 
    
    for(i in c(19, 24)){
      gp <- g$grobs[[15]]$grobs[[1]]$grobs[[i]]$gp
    
      ind1 <- match(names(gp.new), names(gp))
      ind2 <- match(names(gp), names(gp.new))
      ind2 <- ind2[!is.na(ind2)]
    
      g$grobs[[15]]$grobs[[1]]$grobs[[i]]$gp <- replace(x = gp,
                                                        list = ind1,
                                                        values = gp.new[ind2])
    }
    rm(gp, gp.new, ind1, ind2, i)
    

    绘制结果。请注意,要绘制 grob,您需要使用 grid 包中的grid.draw()

    grid::grid.draw(g)
    

    【讨论】:

    • 非常感谢。它工作得很好,你解释得很好。还有一件事:有没有办法增加 only(!) "Object" 和 "Lines" 的字体大小?
    • @Wruke 用修改后的图例标签更新了我的答案。
    猜你喜欢
    • 1970-01-01
    • 2013-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 2021-08-10
    • 2023-04-05
    • 2016-10-23
    相关资源
    最近更新 更多