Reputation: 54
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?
Upvotes: 1
Views: 1239
Reputation: 32829
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)
Upvotes: 1
Reputation: 1634
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
Upvotes: 1
Reputation: 32829
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
Upvotes: 2