简体   繁体   中英

r ggplot2 legend: how to display linetype below symbol?

I am plotting a figure that uses both symbols and linetype to differentiate between groups (points and 95% confidence ellipses, respectively).

Here is an example plot with a similar legend:

  bplot<-ggplot(iris,aes(x=Sepal.Length,y=Sepal.Width,group=Species,shape=Species,lty=Species))+
    geom_point(size=3)+geom_smooth(method="lm",se=F,color="black")+
    theme_minimal()+theme(legend.key.size=unit(1.2,"cm"))
  bplot

The problem with this is that the linetypes are hard to see in the legend because they are overlapping the symbols. Is there a way to display the linetype in the same legend as the symbols (with a single label), but below the symbols or in a way that both are readable?

You need the guides functionality. Please go through the documentation for more customization.

bplot<-ggplot(iris,aes(x=Sepal.Length,y=Sepal.Width,group=Species,shape=Species,lty=Species))+
  geom_point(size=3)+geom_smooth(method="lm",se=F,color="black")+
  theme_minimal()+theme(legend.key.size=unit(1.2,"cm")) + 
  guides(
    shape = guide_legend(order = 1),
    size = guide_legend(order = 2)
  )
bplot

在此处输入图片说明

I think the legend is readable if the keys are widened a little.

library(ggplot2)

bplot <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, group = Species,
                          shape = Species,lty = Species)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = F, color = "black") +
  theme_minimal() +
  theme(legend.key.width = unit(1.5, "cm"))

bplot

在此处输入图片说明

But if you want to separate the point from the line within each key, then I think you will need to delve into the ggplot grob.

library(grid)

bplot <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, group = Species,
                          shape = Species,lty = Species)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = F, color = "black") +
  theme_minimal() +
  theme(legend.key.width = unit(1.5, "cm"),
        legend.key.height = unit(1, "cm"),
        legend.key = element_rect(colour = "grey50", size = .5))

# Get the plot grob
g = ggplotGrob(bplot)

# Get the legend
leg = g$grobs[[which(g$layout$name == "guide-box")]]

# Get the relevant keys
pos = grep("key-.-1-1", leg$grobs[[1]]$layout$name)
# pos gets the point; pos+1 gets the line

# Separate the line from the point within each key
for(i in pos) {
   leg$grobs[[1]]$grobs[[i]]$y = unit(0.6, "npc")

   leg$grobs[[1]]$grobs[[i+1]]$children[[1]]$y0 = unit(0.3, "npc")
   leg$grobs[[1]]$grobs[[i+1]]$children[[1]]$y1 = unit(0.3, "npc")
}

# Put the legend back into the plot
g$grobs[[which(g$layout$name == "guide-box")]] = leg

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

在此处输入图片说明

Or, if you want separate legends, see @Divi's answer

One option for editing specific grobs within a ggplot grob is given in one of the posts here. It involves following a pathway through long lists within lists to get to the relevant grob. Sometimes, it is easier (although maybe only marginally so) to edit the relevant grobs using grid editing functions.

library(ggplot2)
library(grid)

# The plot
bplot <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, group = Species,
                          shape = Species,lty = Species)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = F, color = "black") +
  theme_minimal() +
  theme(legend.key.width = unit(1.5, "cm"),
        legend.key.height = unit(1, "cm"),
        legend.key = element_rect(colour = "grey50", size = .5))

# Get the plot grob
g = ggplotGrob(bplot)

# Get a list of the grobs 
grid.ls(grid.force(g)) 

Look through the list of grobs. The grobs we want to edit are towards the bottom of the list - with names that begin with "key". There are three groups of three - three groups because there are three keys in the legend. Within each group, there are three grobs:

key-3-1-bg.4-2-4-2
key-3-1-1.4-2-4-2
key-3-1-2.4-2-4-2
GRID.segments.819

The first is the background - of no interest here.
The second refers to the point - we want to edit its vertical position.
The third refers to a grob that contains a child - GRID.segments - the line segments. We want to edit the line segment's vertical position.

In the editGrob() command, gPath lists the grob to be edited, but grep=TRUE means that regular expressions can be use, and global=TRUE means all matches will be affected. So, only one command for each edit.

# Edit the 'key' grobs
# Move the point up a little
g = editGrob(grid.force(g), gPath("key-[1-9]-1-1"), grep = TRUE, global = TRUE,  
         y = unit(0.6, "npc"))

# Move the line segment down a little
g = editGrob(grid.force(g), gPath("GRID.segments"), grep = TRUE, global = TRUE,  
         y0 = unit(0.3, "npc"), y1 = unit(0.3, "npc"))

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

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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