[英]Match legend text color in geom_text to symbol
我正在尝试使用geom_text
将图例的文本颜色与因子变量生成的文本颜色匹配。 这是一个最小的工作示例:
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
我相信这是一个简单的修复。 任何建议或对先前帖子的引用都会有所帮助。 我没有找到任何特定于此的东西。
继上面的joran评论之后,你可以直接编辑grobs。 这是一个相当丑陋的代码,所以道歉[将有一个更优雅的方式来使用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)
}
情节
gglabcol(p1)
有时使用grid
的编辑功能编辑 grob 会更容易 - 如果可以找到相关 grob 的名称。 在这种情况下,可以找到它们,并且编辑很简单 - 将标签颜色从黑色更改为红色或蓝色。
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))
查看grobs列表。 我们要编辑的 grob 位于列表底部,位于 'guide-box' grob 组中 - 名称以“label”开头。 有两个grob:
标签-3-3.4-4-4-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)
如果要求键是点而不是字母怎么办? 它可能很有用,因为 'a' 是图中的一个符号,它是图例键中的一个符号。 这不是一个简单的编辑,就像上面一样。 我需要一个点 grob 来代替文本 grob。 我在视口中绘制了 grob,但如果我能找到相关视口的名称,那么进行更改应该很简单。
# Find the names of the relevant viewports
current.vpTree() # Scroll out to the right to find he relevant 'key' viewports.
视口[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)
更新
考虑到@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
然后像以前一样继续更改图例文本的颜色。
绘图中的颜色与图例中的颜色相同,但即使将绘图符号字体设置为粗体(或斜体),图例字体仍保持简单。 我不确定这是否是ggplot2
设计中的ggplot2
或预期行为。 对于某些颜色,粗体字体看起来比普通字体更饱和,使其看起来像是不同的颜色。
无论如何,这里有一个杂乱无章的东西,它比弄乱grob要容易得多,但这可能会让你得到你想要的。 将geom_text
与普通geom_text
一起使用,但要连续执行两到三次(或多次),这样您会得到geom_text
。 这将使符号和图例看起来都类似于粗体,因为两者都会被过度绘制,并且图例符号将始终与绘图符号相同。
下面是一个例子:
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")
这是一个使用ggtext
并避免直接编辑 grobs 的解决方案。 (它确实涉及从图中提取颜色,但后续步骤更加用户友好。)
# 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())
这个答案是基于Mike H.从这里的问题和user20650从这个问题给出的答案。
获取颜色:
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)
然后使用以下方法
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)
之后,您将获得相同的输出文件
grid.draw(pGrob)
我提供这个答案是因为在代码的第二个块中,在mapply
函数中分配给x$gp
的mapply
应该是一个gpar
对象。 这是Mike H.的答案中的一个错误,我正在修复它。
谢谢你。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.