ggplot2 可以在一个图例中分别控制点大小和线宽(线宽)吗?
Posted
技术标签:
【中文标题】ggplot2 可以在一个图例中分别控制点大小和线宽(线宽)吗?【英文标题】:Can ggplot2 control point size and line size (lineweight) separately in one legend? 【发布时间】:2014-09-20 08:29:20 【问题描述】:使用ggplot2
绘制数据点组和连接每个组均值的线的示例,使用相同的aes
映射shape
和linetype
:
p <- ggplot(mtcars, aes(gear, mpg, shape = factor(cyl), linetype = factor(cyl))) +
geom_point(size = 2) +
stat_summary(fun.y = mean, geom = "line", size = 1) +
scale_shape_manual(values = c(1, 4, 19))
问题是图例中的点符号相对于线符号显得有点太小而无法看到:
尝试enlarge point size in legend 也会增大线宽,所以这里没有用。
p1 <- p + guides(shape = guide_legend(override.aes = list(size = 4)))
如果线宽是 size
的独特美学,那就太好了。
我尝试添加
+ guides(linetype = guide_legend(override.aes = list(size = 1)))
这只是一个警告。
> Warning message:
In guide_merge.legend(init, x[[i]]) : Duplicated override.aes is ignored.
如果我将linetype
aes
移出ggplot()
并移入stat_summary()
,这似乎没有什么区别。如果我只想要点符号,我可以从图例中删除线 this way。
p2 <- p + guides(shape = guide_legend(override.aes = list(size = 4, linetype = 0)))
相反,(在图表本身中保留小点符号)我想要 一个单个 图例,其中 both 大点符号就像最后一张图片中一样和 em> 细线符号,如第一张图片所示。有没有办法做到这一点?
【问题讨论】:
【参考方案1】:独立设置这些属性似乎确实很困难。我只能想出一个技巧。如果您的真实数据有很大不同,则可能需要对其进行调整。但我所做的是使用override.aes
来设置点的大小。然后我进去建图,然后在实际的低级网格对象中手动更改线宽设置。这是代码
pp<-ggplot(mtcars, aes(gear, mpg, shape = factor(cyl), linetype = factor(cyl))) +
geom_point(size = 3) +
stat_summary(fun.y = mean, geom = "line", size = 1) +
scale_shape_manual(values = c(1, 4, 19)) +
guides(shape=guide_legend(override.aes=list(size=5)))
build <- ggplot_build(pp)
gt <- ggplot_gtable(build)
segs <- grepl("geom_path.segments", sapply(gt$grobs[[8]][[1]][[1]]$grobs, '[[', "name"))
gt$grobs[[8]][[1]][[1]]$grobs[segs]<-lapply(gt$grobs[[8]][[1]][[1]]$grobs[segs],
function(x) x$gp$lwd<-2; x)
grid.draw(gt)
神奇的数字“8”是gt$grobs[[8]]$name=="guide-box"
的位置,所以我知道我正在为传奇工作。我在网格图形和 gtables 方面还不是最好的,所以也许有人可以提出更优雅的方法。
【讨论】:
+1 用于深入研究。至少你给了我一种程序化的方式来做到这一点——我已经厌倦了在 r 之外编辑每个情节只是为了调整图例。我会在其他数据上尝试您的方法,看看在此期间是否有其他想法出现。 在进一步尝试之后,我现在明白你为什么说“幻数”了。所以这里的诀窍是如何索引那个“指南框”,它并不总是 grobs 列表中的第 8 个元素。除了那种不便之外,这种方法给出了我在单个图例中寻找的结果。 您可以创建一个辅助函数来进行查找。例如getgtable<-function(n) which(sapply(gt$grobs, `[[`, "name")==n); getgtable("guide-box")
这对于辅助函数现在非常有用。它也适用于绘图功能。 This question 帮助我保存了 grob
输出。
警告说明:更复杂的是,geom_path.segments
有时可能隐藏在gTree
中(因此在此答案的sapply()
命令中找不到)。我花了很长时间才弄清楚如何在gTree
中访问嵌套的grobs
;他们是它的children
。所以语法看起来像gt$grobs[[mn]][[1]][[1]]$grobs[[i]]$children
,其中i
在gt
中为每个grob
命名为"GRID.gTree"
。然后可以迭代答案中使用的类似过程以调整嵌套的segments
。【参考方案2】:
使用 grid
函数 grid.force()
,ggplot 中的所有 grobs 都对 grid's
编辑函数可见,包括图例键。因此,可以应用grid.gedit
,并且可以使用一行代码实现所需的绘图编辑。另外,我增加了图例键的宽度,使线段的不同线型清晰。
library(ggplot2)
library(grid)
p <- ggplot(mtcars, aes(gear, mpg, shape = factor(cyl), linetype = factor(cyl))) +
geom_point(size = 2) +
stat_summary(fun.y = mean, geom = "line", size = 1) +
scale_shape_manual(values = c(1, 4, 19)) +
theme(legend.key.width = unit(1, "cm"))
p
grid.ls(grid.force()) # To get the names of all the grobs in the ggplot
# The edit - to set the size of the point in the legend to 4 mm
grid.gedit("key-[-0-9]-1-1", size = unit(4, "mm"))
保存修改后的绘图
g <- grid.grab()
ggsave(plot=g, file="test.pdf")
【讨论】:
您的建议有效,但是当我使用ggsave(plot=p, file="test.pdf")
将图形保存为 PDF 时,符号与应用 grid.gedit()
之前一样。
@Valentine 那是因为您保存的是原始 ggplot 对象,而不是修改后的屏幕网格对象。一个方便的方法是 grid.grab() 屏幕上的对象,然后保存。即在save命令前插入g
【参考方案3】:
我明白你的意思。我认为,这是一个适合您正在寻找的解决方案。它将两个图例分开,但将它们并排放置。形状的标签和标题被省略,因此最右边的标签对应于形状和线型。
我将此作为单独的答案发布,因为我认为这两种方法都对未来的读者有效。
p2 <- ggplot(mtcars, aes(gear, mpg, shape = factor(cyl),
linetype = factor(cyl))) +
geom_point(size = 2) +
stat_summary(fun.y = mean, geom = "line", size = 1) +
# blank labels for the shapes
scale_shape_manual(name="", values = c(1, 4, 19),
labels=rep("", length(factor(mtcars$cyl))))+
scale_linetype_discrete(name="Cylinders")+
# legends arranged horizontally
theme(legend.box = "horizontal")+
# ensure that shapes are to the left of the lines
guides(shape = guide_legend(order = 1),
linetype = guide_legend(order = 2))
p2
【讨论】:
将两个图例伪装成一个是一个聪明的主意。它允许为每个图例独立设置override.aes
等。但是,由于线条不覆盖点符号,这可能变得不那么必要了。将图例标题偏移到一侧有点令人不满意 - 它看起来会更好地居中,只是为了挑剔。但是感谢这个解决方案。【参考方案4】:
确保单独的图例的一种方法是为它们指定不同的名称(或其他会妨碍它们组合在一起的差异)。
这是基于您提供的代码的示例:
p <- ggplot(mtcars, aes(gear, mpg, shape = factor(cyl), linetype = factor(cyl))) +
geom_point(size = 2) +
stat_summary(fun.y = mean, geom = "line", size = 1) +
scale_shape_manual(name="Name 1", values = c(1, 4, 19))+
scale_linetype_discrete(name="Name2")
p
【讨论】:
没错,这通常很有用。但在这里,我希望保留一个传奇。毕竟,如果“名称 1”和“名称 2”都指代相同的因子(在本例中为 cyl),它们有何不同?以上是关于ggplot2 可以在一个图例中分别控制点大小和线宽(线宽)吗?的主要内容,如果未能解决你的问题,请参考以下文章
R语言ggplot2可视化自定义图例(legend)方框(box):所有图例没有方框每个图例分别在不同的方框中多个图例放置在同一个方框中