Can ggplot2 control point size and line size (lineweight) separately in one legend?

旧时模样 提交于 2019-11-27 23:03:49

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.

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

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

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!