Suppose I want to plot a graph with both points and lines where points appear in front of their corresponding lines in each group. In particular, I want group 1 to be plotted with red filled points, where the points are connected by a line, but group 2 to be plotted with (just) a blue line, but I want group 2 to be plotted over group 1. For example, in base graphics:
set.seed(101)
dd <- data.frame(x=rep(1:10,2),
y=rep(1:10,2),
f=factor(rep(1:2,each=10)))
dd$y[11:20] <- dd$y[11:20] + rnorm(10)
d1 <- subset(dd,f=="1")
d2 <- subset(dd,f=="2")
par(cex=1.5)
plot(y~x,data=d1,bg="red",pch=21,type="o")
lines(y~x,data=d2,col="blue",lwd=2)
legend("bottomright",c("group 1","group 2"),
col=c("black","blue"),
pch=c(21,NA),
pt.bg=c("red",NA),
lty=1,
lwd=c(1,2))
(My real data are a little more complex.) I'm going a little nuts trying to do this cleanly in ggplot2
.
If I draw points before lines, group 1's points get overlaid by the lines in the same group:
library(ggplot2); theme_set(theme_bw())
g0 <- ggplot(dd,aes(x,y,fill=f,colour=f,shape=f))+
scale_fill_manual(values=c("red",NA))+
scale_colour_manual(values=c("black","blue")) +
scale_shape_manual(values=c(21,NA))
g0 + geom_point()+ geom_line()
ggsave("order2.png",width=3,height=3)
If I draw lines before points, group 2's lines get overlaid by group 1's points:
g0 + geom_line()+ geom_point()
ggsave("order3.png",width=3,height=3)
The desired order is (group 1 lines), (group 1 points), (group 2 lines). I can do this by manually overlaying the geoms again, one group at a time, but this is way ugly.
g0 + geom_line() + geom_point()+
geom_point(data=d1)+
geom_line(data=d2,show.legend=FALSE)
ggsave("order4.png",width=3,height=3)
I think the "best" solution to this is to write a low-level geom_linepoint
that works as desired; I've looked into this a bit and it's not entirely trivial ... can anyone suggest a cleaner, simpler solution?
Here's a "low tech" 1 solution. Below is a function that adds a line layer and then a point layer successively for each level of a given grouping variable.
linepoint = function(data, group.var, lsize=1.2, psize=4) {
lapply(split(data, data[,group.var]), function(dg) {
list(geom_line(data=dg, size=lsize),
geom_point(data=dg, size=psize))
})
}
ggplot(dd, aes(x,y, fill=f, colour=f,shape=f))+
scale_fill_manual(values=c("red",NA))+
scale_colour_manual(values=c("black","blue")) +
scale_shape_manual(values=c(21,NA)) +
linepoint(dd, "f")
1 "Low tech" compared to writing a new geom. @baptiste's (now deleted) answer does create a new geom and seems to get the job done, so I'm not sure why he deleted it.
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.