为一个ggplot2图创建两个图例并对其进行修改 [英] Create two legends for one ggplot2 graph and modify them

查看:193
本文介绍了为一个ggplot2图创建两个图例并对其进行修改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

 #生成数据X1 <-as.data.frame(矩阵(nrow = 10,ncol = 2))colnames(X1)<-c("Weight","Height")X1 $重量<-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(矩阵(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(矩阵(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)#创建情节要求(ggplot2)mycurve<-qplot(重量,高度,数据= X1,geom =线",颜色="X1",main ="Plot",xlab =重量[kg]",ylab =高度[m]",xlim =范围(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" =黑色","X2" ="darkseagreen4","X3" =暗红色","Y1" =绿色2","Y2" =蓝色"))+theme_bw() 

它给了我这个输出:

现在,我想更改图例.我想拥有两个传说:

  • 一个名为"Object"的人,给我"Object1"(X1),"Object2"(X2)和"Object3"(X3)
  • 另一个标题为线"并包含线1"(Y1)和线2"(Y2)

此外,我希望对象"具有以下顺序:对象2",对象1",对象3".

对于解决此问题,我将不胜感激!

解决方案

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

很整齐

 #合并数据df<-rbind(X1,X2,X3)df $ Group<-rep(c("Object1","Object2","Object3"),每个= 10)df<-rbind(df,data.frame(重量= 5,高度= c(0,X1 ["5",2]),组=第1行"),data.frame(重量= 7,高度= c(0,X1 ["7",2]),组=第2行")) 

在ggplot中,根据设计,每种刻度类型都有一个图例,因此自然不会出现两个线色图例.帖子

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

我们可以对单个图例标签进行进一步更改,以使伪图例标题与其他常规"标签更加不同.由于ggplot的图例并非旨在处理此用例,因此可以通过将图(ggplot2对象)转换为grob对象(本质上是图形对象的嵌套列表)来破解它.在此处进行修改:

 #将原始图(另存为p)转换为grobg<-ggplotGrob(p) 

找到与图例标签相对应的嵌套grob(有多种方法可以使用代码按关键字进行搜索,但是对于一次性使用案例,我发现遍历列表更容易且更清晰...):

 >g#grob 15(命名为指南盒)包含图例TableGrob(10 x 9)版式":18个粗笔z单元名称grob1 0(1-10,1- 9)背景rect [plot.background..rect.174]2 5(5- 5,3- 3)spacer zeroGrob [NULL]3 7(6-6-3-3)轴-l absoluteGrob [GRID.absoluteGrob.124]4 3(7- 7,3- 3)spacer zeroGrob [NULL]5 6(5- 5,4- 4)轴-t zeroGrob [NULL]6 1(6-6,4- 4)面板gTree [panel-1.gTree.104]7 9(7- 7,4- 4)轴b b absoluteGrob [GRID.absoluteGrob.117]8 4(5- 5,5- 5)spacer zeroGrob [NULL]9 8(6-6,5-5)轴-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)导引盒gtable [guide-box]16 15(3- 3,4- 4)字幕zeroGrob [plot.subtitle..zeroGrob.171]17 16(2- 2,4- 4)标题titleGrob [plot.title..titleGrob.170]18 17(9- 9,4- 4)字幕zeroGrob [plot.caption..zeroGrob.172]>g $ grobs [[15]]#grob 1(名为指南)包含实际的图例表TableGrob(5 x 5)指南盒":2个杂点z单元名称grob99_ff1a4629bd4c693e1303e4eecfb18bd2 1(3-3,3-3)引导gtable [layout]0(2-4,2-4)legend.box.background zeroGrob [NULL]>g $ grobs [[15]] $ grobs [[1]]#grobs 19-25包含图例标签TableGrob(12 x 6)"layout":26个grobsz单元名称grob1 1(1-12,1- 6)background rect [legend.background..rect.167]2 2(2- 2,2- 5)标题zeroGrob [guide.title.zeroGrob.125]3 3(4- 4,2- 2)键-3-1-bg rect [legend.key..rect.143]4 4(4- 4,2- 2)键3-1-1分段[GRID.segments.144]5 5(5- 5,2- 2)键-4-1-bg rect [legend.key..rect.146]6 6(5- 5,2- 2)键4-1-1分段[GRID.segments.147]7 7(6- 6,2- 2)键-5-1-bg rect [legend.key..rect.149]8 8(6- 6-2-2)键5-1-1段[GRID.segments.150]9 9(7- 7,2- 2)键-6-1-bg rect [legend.key..rect.152]10 10(7- 7,2- 2)键6-1-1段[GRID.segments.153]11 11(8- 8,2- 2)key-7-1-bg rect [legend.key..rect.155]12 12(8- 8,2- 2)键7-1-1段[GRID.segments.156]13 13(9- 9,2- 2)键-8-1-bg rect [legend.key..rect.158]14 14(9- 9,2-2)键8-1-1段[GRID.segments.159]15 15(10-10,2- 2)键-9-1-bg rect [legend.key..rect.161]16 16(10-10,2- 2)键9-1-1段[GRID.segments.162]17 17(11-11,2- 2)key-10-1-bg rect [legend.key..rect.164]18 18(11-11,2- 2)键-10-1-1段[GRID.segments.165]19 19(4- 4,4- 4)label-3-3文本[guide.label.text.127]20 20(5- 5,4- 4)label-4-3文本[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] 

因此,我们可以找到与"Object"和"行".他们是:

  g $ grobs [[15]] $ grobs [[1]] $ grobs [[19]]#对象"的标签g $ grobs [[15]] $ grobs [[1]] $ grobs [[24]]#行的标签>str(g $ grobs [[15]] $ grobs [[1]] $ grobs [[19]])#检查标签清单11$标签:chr对象"$ x:类单元"原子[1:1] 0.. ..- attr(*,"valid.unit")= int 0.. ..- attr(*,"unit")= chr"npc"$ y:单元"类别原子[1:1] 0.5.. ..- attr(*,"valid.unit")= int 0.. ..- attr(*,"unit")= chr"npc"$ just:chr中心"$恰好:num 0$ vjust:num 0.5$腐烂:num 0$ check.overlap:logi FALSE$名称:chr"guide.label.text.214"$ gp:列表5.. $ fontsize:8.8.. $ col:chr"black".. $ fontfamily:chr".. $ lineheight:num 0.9.. $字体:命名为int 1.. ..- attr(*,"names")= chr"plain"..- attr(*,"class")= chr"gpar"$ vp:空-attr(*,"class")= chr [1:3]"text""grob""gDesc" 

我们可以看到格式捕获在.$ gp 下(图形参数列表,请参见

I have some trouble with modifying the legend in a ggplot2 graph. Specifically, I want to create two legends instead of one and change the names inside.

I want to plot three curves together with two vertical lines that range from the x-axis to the first of the curves. The code I used is the following:

# 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()

It gives me this output:

Now I want to change the legend. I want to have two legends:

  • one titled "Object" and giving me "Object1" (X1), "Object2" (X2) and "Object3" (X3)
  • the other one titled "Lines" and containing "Line1" (Y1) and "Line2" (Y2)

Further, I want "Objects" to have the following order: "Object2", "Object1", "Object3".

I would appreciate any help on solving this!

解决方案

I recommend combining your data into a single data frame. It's neater for passing to 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"))

In ggplot, we have one legend for each scale type by design, so having two legends of line colours isn't something that comes naturally. The post here discusses some approaches. I made use of the 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"))

In addition, I suggest using ggplot rather than qplot. As noted by the package's documentation, qplot is designed as a convenient wrapper for consistency with the base plot function's syntax, but ggplot is better at handling more complex plot requirements:

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

Edit to include changing individual legend labels:

We can make further changes to individual legend labels, in order to make the pseudo legend titles more distinct from the other 'normal' labels. Since ggplot's legend wasn't designed to handle this use case, we can hack it by turning the plot (a ggplot2 object) into a grob object (essentially a nested list of graphical objects), & make modifications there:

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

Find the nested grob corresponding to the legend labels (there are ways to do it using code to search by keywords, but for a one-off use case, I find it easier & clearer to look through the list...):

> 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]

We can thus locate the grobs corresponding to "Object" & "Lines". They are:

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"

We can see that formatting is captured under .$gp (a list of graphical parameters, see here for more info). We can make a list of changes, & replace them in the original list for each label:

# 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)

Plot the result. Note that to plot a grob, you need to use grid.draw() from the grid package:

grid::grid.draw(g)

这篇关于为一个ggplot2图创建两个图例并对其进行修改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆