可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
An example using ggplot2
to graph groups of data points and lines connecting the means for each group, mapped with the same aes
for shape
and for 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))
Problem is that point symbols in the legend appear a bit too small to see, relative to the line symbols:
Trying to enlarge point size in legend also enlarges lineweight, so that is not useful here.
p1 <- p + guides(shape = guide_legend(override.aes = list(size = 4)))
It would be nice if lineweight were a distinct aesthetic from size
.
I tried adding
+ guides(linetype = guide_legend(override.aes = list(size = 1)))
which just gives a warning.
> Warning message:
In guide_merge.legend(init, x[[i]]) : Duplicated override.aes is ignored.
It seems to make no difference either if I move the linetype
aes
out of ggplot()
and into stat_summary()
. If I wanted only the point symbols, I could eliminate lines from the legend this way.
p2 <- p + guides(shape = guide_legend(override.aes = list(size = 4, linetype = 0)))
Instead, (keeping small point symbols in the graph itself) I want one single legend with both big point symbols as in this last image and thin line symbols as in the first image. Is there a way to do this?
回答1:
It sure does seem to be difficult to set those properties independently. I was only kind of able to come up with a hack. If your real data is much different it will likely have to be adjusted. But what i did was used the override.aes
to set the size of the point. Then I went in and built the plot, and then manually changed the line width settings in the actual low-level grid objects. Here's the code
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)
The magic number "8" was where gt$grobs[[8]]$name=="guide-box"
so i knew I was working the legend. I'm not the best with grid graphics and gtables yet, so perhaps someone might be able to suggest a more elegant way.
回答2:
Using the grid
function grid.force()
, all the grobs in the ggplot become visible to grid's
editing functions, including the legend keys. Thus, grid.gedit
can be applied, and the required edit to the plot can be achieved using one line of code. In addition, I increase the width of the legend keys so that the different line types for line segments are clear.
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"))
To save the modified plot
g <- grid.grab()
ggsave(plot=g, file="test.pdf")
回答3:
I see what you mean. Here is a solution that fits what you're looking for, I think. It keeps both of the legends separate, but places them side by side. The labels and title of the shape are left out, so that the labels to the far right correspond to both the shapes and linetypes.
I'm posting this as a separate answer because I think both methods will be valid for future readers.
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
回答4:
One way to ensure separate legends is to give them different names (or other differences that would preclude them being grouped together).
Here's an example based on the code you supplied:
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