简体   繁体   English

将 geom_text 中的图例文本颜色与符号匹配

[英]Match legend text color in geom_text to symbol

I am trying to color match the text of the legend to the color of text produced by a factored variable using geom_text .我正在尝试使用geom_text将图例的文本颜色与因子变量生成的文本颜色匹配。 Here is a minimal working example:这是一个最小的工作示例:

df <- data.frame(a=rnorm(10),b=1:10,c=letters[1:10],d=c("one","two"))
p1 <-ggplot(data=df,aes(x=b,y=a))
p1 <- p1 + geom_text(aes(label = c, color=d, fontface="bold"))
p1 <- p1 + scale_color_hue(name="colors should match",breaks=c("one", "two"),
                 labels=c("should be pink", "should be blue"))
p1

在此处输入图片说明

I am sure its a simple fix.我相信这是一个简单的修复。 Any suggestions or reference to prior posts would help.任何建议或对先前帖子的引用都会有所帮助。 I did not find anything specific to this.我没有找到任何特定于此的东西。

Following on from joran's comment above, you can edit the grobs directly. 继上面的joran评论之后,你可以直接编辑grobs。 This is a rather ugly bit of code so apologies [there will be a much more elegant way to do this using grid commands - and hopefully someone will post]. 这是一个相当丑陋的代码,所以道歉[将有一个更优雅的方式来使用grid命令这样做 - 希望有人会发布]。

library(grid)

gglabcol <- function(plot1){

         g <- ggplotGrob(plot1)

         # legend grobs
         g.b <- g[["grobs"]][[which(g$layout$name=="guide-box")]]
         l <- g.b[[1]][[1]][["grobs"]]

         # get grobs for legend symbols (extract colour)
         lg <- l[sapply(l, function(i) grepl("GRID.text", i))]

         # get grobs for legend labels 
         lb <- g.b[[1]][[1]][["grobs"]][grepl("label", g.b[[1]][[1]]$layout$name)]

         # get change colour of labels to colour of symbols
         for(i in seq_along(lg)) {

           g.b[[1]][[1]][["grobs"]][grepl("label", g.b[[1]][[1]]$layout$name)][[i]][["children"]][[1]][["children"]][[1]]$gp$col <- lg[[i]]$gp$col

           }

         # overwrite original legend
         g[["grobs"]][[which(g$layout$name=="guide-box")]] <- g.b

         grid.draw(g)

         invisible(g)
   }

Plot 情节

gglabcol(p1)

在此输入图像描述

Sometimes it is easier to edit a grob using grid 's editing functions - if the names of the relevant grobs can be found.有时使用grid的编辑功能编辑 grob 会更容易 - 如果可以找到相关 grob 的名称。 In this case, they can be found, and the edit is straightforward - change colour of label from black to red or blue.在这种情况下,可以找到它们,并且编辑很简单 - 将标签颜色从黑色更改为红色或蓝色。

library(ggplot2)
library(grid)

df <- data.frame(a=rnorm(10),b=1:10,c=letters[1:10],d=c("one","two"))
p1 <-ggplot(data=df,aes(x=b,y=a))
p1 <- p1 + geom_text(aes(label = c, color=d, fontface="bold"))
p1 <- p1 + scale_color_hue(name="colors should match",breaks=c("one", "two"),
                 labels=c("should be salmon", "should be sky blue"))
p1

# Get the ggplot grob
g <- ggplotGrob(p1)

# Check out the grobs
grid.ls(grid.force(g))

Look through the list of grobs.查看grobs列表。 The grobs we want to edit are towards the bottom of the list, in the 'guide-box' set of grobs - with names that begin with "label".我们要编辑的 grob 位于列表底部,位于 'guide-box' grob 组中 - 名称以“label”开头。 There are two grobs:有两个grob:

label-3-3.4-4-4-4标签-3-3.4-4-4-4
label-4-3.5-4-5-4 label-4-3.5-4-5-4

# Get names of 'label' grobs.
names.grobs <- grid.ls(grid.force(g))$name 
labels <- names.grobs[which(grepl("label", names.grobs))]

# Get the colours
# The colours are the same as the colours of the plotted points.
# These are available in the ggplot build data.
gt <- ggplot_build(p1)
colours <- unique(gt$data[[1]][, "colour"])

# Edit the 'label' grobs - change their colours
# Use the `editGrob` function
for(i in seq_along(labels)) {
    g <- editGrob(grid.force(g), gPath(labels[i]), grep = TRUE,  
         gp = gpar(col = colours[i]))
}

# Draw it
grid.newpage()
grid.draw(g)

在此处输入图片说明

What if it was required that the keys be points rather than letters?如果要求键是点而不是字母怎么办? It could be useful because the 'a' is a symbol in the plot, and it is a symbol in the legend key.它可能很有用,因为 'a' 是图中的一个符号,它是图例键中的一个符号。 This is not a simple edit, like above.这不是一个简单的编辑,就像上面一样。 I need a point grob to take the place of the text grob.我需要一个点 grob 来代替文本 grob。 I draw grobs in viewports, but if I can find the names of the relevant viewports, it should be straightforward to make the change.我在视口中绘制了 grob,但如果我能找到相关视口的名称,那么进行更改应该很简单。

# Find the names of the relevant viewports
current.vpTree()  # Scroll out to the right to find he relevant 'key' viewports.

viewport[key-4-1-1.5-2-5-2], viewport[key-3-1-1.4-2-4-2],视口[key-4-1-1.5-2-5-2],视口[key-3-1-1.4-2-4-2],

# Well, this is convenient. The names of the viewports are the same 
# as the names of the grobs (see above). 
# Easy enough to get the names from the 'names.grobs' list (see above). 
# Get the names of 'key' viewports(/grobs)
keys <- names.grobs[which(grepl("key-[0-9]-1-1", names.grobs))]

# Insert points grobs into the viewports:
#    Push to the viewport;
#    Insert the point grob;
#    Pop the viewport.
for(i in seq_along(keys)) {
   downViewport(keys[i])
   grid.points(x = .5, y = .5, pch = 16, gp = gpar(col = colours[i]))
   popViewport()
}
popViewport(0)

# I'm not going to worry about removing the text grobs. 
# The point grobs are large enough to hide them. 

plot = grid.grab()
grid.newpage()
grid.draw(plot)

在此处输入图片说明

Update更新

Taking account of @user20650 's advice to change the legend key (see the comment below):考虑到@user20650 更改图例键的建议(请参阅下面的评论):

p1 <-ggplot(data=df,aes(x=b,y=a))
p1 <- p1 + geom_text(aes(label = c, color=d, fontface="bold"))
p1 <- p1 + scale_color_hue(name="colors should match",breaks=c("one", "two"),
                 labels=c("should be salmon", "should be sky blue"))

GeomText$draw_key <- function (data, params, size) { 
   pointsGrob(0.5, 0.5, pch = 16, 
   gp = gpar(col = alpha(data$colour, data$alpha), 
   fontsize = data$size * .pt)) }

p1

Then proceed as before to change the colour of the legend text.然后像以前一样继续更改图例文本的颜色。

The colors in the plot are the same as the colors in the legend, but the legend fontface remains plain even when you set the plot symbol fontface to bold (or italic).绘图中的颜色与图例中的颜色相同,但即使将绘图符号字体设置为粗体(或斜体),图例字体仍保持简单。 I'm not sure if this is an oversight in the design of ggplot2 or the intended behavior.我不确定这是否是ggplot2设计中的ggplot2或预期行为。 For some colors, the bold fontface looks more saturated than the plain fontface, making it seem like a different color.对于某些颜色,粗体字体看起来比普通字体更饱和,使其看起来像是不同的颜色。

In any case here's a kludge that's a lot easier than messing with grobs, but that might get you what you want.无论如何,这里有一个杂乱无章的东西,它比弄乱grob要容易得多,但这可能会让你得到你想要的。 Use geom_text with the plain fontface, but do it two or three times in a row (or more), so you'll get overplotting.geom_text与普通geom_text一起使用,但要连续执行两到三次(或多次),这样您会得到geom_text This will make both the symbols and the legend appear similar to bold fontface, because both will be overplotted, and the legend symbols will always look the same as the plot symbols.这将使符号和图例看起来都类似于粗体,因为两者都会被过度绘制,并且图例符号将始终与绘图符号相同。

Here's an example:下面是一个例子:

library(ggplot2)
library(gridExtra)

# Original plot (with larger font size)
p1 <- ggplot(data=df) +
  geom_text(aes(x=b, y=a, label=c, color=d), fontface='bold', size=8)
p1 <- p1 + scale_color_hue(name="colors should match",breaks=c("one", "two"),
                              labels=c("should be pink", "should be blue")) +
           ggtitle("Original Plot with Bold Symbols and Plain Legend")

# New version with overplotting. (You don't need to specify 'plain' fontface. 
# I've just included that to emphasize what the code is doing.)
p1.overplot <- ggplot(data=df) +
  geom_text(aes(x=b, y=a, label=c, color=d), fontface='plain', size=8) +
  geom_text(aes(x=b, y=a, label=c, color=d), fontface='plain', size=8) +
  geom_text(aes(x=b, y=a, label=c, color=d), fontface='plain', size=8)
p1.overplot <- p1.overplot + 
  scale_color_hue(name="colors should match",
                  breaks=c("one", "two"),
                  labels=c("should be pink", "should be blue")) +
  ggtitle("Both symbols and legend are overplotted 3 times")

在此处输入图片说明

Here's a solution that uses ggtext and avoids editing grobs directly.这是一个使用ggtext并避免直接编辑 grobs 的解决方案。 (It does involve extracting the colors from the plot, but the subsequent steps are more user-friendly.) (它确实涉及从图中提取颜色,但后续步骤更加用户友好。)

# Original code, but with a stripped-down call to `scale_color_hue` (since
# we're going to replace it).
library(ggplot2)
df <- data.frame(a=rnorm(10),b=1:10,c=letters[1:10],d=c("one","two"))
p1 <-ggplot(data=df,aes(x=b,y=a))
p1 <- p1 + geom_text(aes(label = c, color=d, fontface="bold"))
p1 <- p1 + scale_color_hue(breaks=c("one", "two"))

# Load the `ggtext` library, which lets us style (parts of) text labels.
library(ggtext)
# Build the plot so we can extract the colors that were actually used.  (If you
# supply colors manually instead, this step isn't necessary.)
g1 = ggplot_build(p1)
# Add a scale with labels that are colored appropriately, using <span> tags.
# Also specify that legend labels should be processed with `element_markdown`.
p1 +
  scale_color_hue(name = "colors should match",
                  breaks = c("one", "two"),
                  labels = paste("<span style='color:",
                                 unname(unlist(unique(g1$data[[1]]["colour"]))),
                                 "'>",
                                 c("should be pink", "should be blue"),
                                 "</span>",
                                 sep = "")) +
  theme(legend.text = element_markdown())

在此处输入图片说明

This answer is based on the answers given by Mike H. from the question here and by user20650 from this question.这个答案是基于Mike H.这里问题user20650从这个问题给出的答案。

To get the colours:获取颜色:

  pGrob <- ggplotGrob(p1)
  g.b   <- pGrob[["grobs"]][[which(pGrob$layout$name=="guide-box")]]
  l     <- g.b[[1]][[1]][["grobs"]]
  # get grobs for legend symbols (extract colour)
  lg    <- l[sapply(l, function(i) grepl("GRID.text", i))]
  clr   <- mapply(FUN=function(x){x$gp$col},x=lg)

Then use the following methods然后使用以下方法

   gb  <- which(grepl("guide-box", pGrob$layout$name))
   gb2 <- which(grepl("guides", pGrob$grobs[[gb]]$layout$name))
   label_text <- which(grepl("label",pGrob$grobs[[gb]]$grobs[[gb2]]$layout$name))

   pGrob$grobs[[gb]]$grobs[[gb2]]$grobs[label_text] <- 
   mapply(FUN = function(x, y) {x[["children"]][[1]][["children"]][[1]]$gp <- gpar(col =y); return(x)},
          x =   pGrob$grobs[[gb]]$grobs[[gb2]]$grobs[label_text],
          y =  clr, SIMPLIFY = FALSE)

After it, you will get the same output file之后,您将获得相同的输出文件

grid.draw(pGrob)

I am providing this answer because in the second chunk of the code, the argument, which is assigned to x$gp in mapply function, should be a gpar object.我提供这个答案是因为在代码的第二个块中,在mapply函数中分配给x$gpmapply应该是一个gpar对象。 That is a mistake in Mike H. 's answer and I am fixing it.这是Mike H.的答案中的一个错误,我正在修复它。

Thank you.谢谢你。

输出图片

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM