Sharing a Legend between two combined ggplots

2019-05-30 02:56发布

问题:

I'm currently trying to present two time series using ggplot2, both with very different scales, using two ggplots. I've combined the two separate ggplots, one on top of the other, using grid.arrange. In order to aid visualization, I'd like to make each line a different colour, and have this legend below the combined plot.

As this may be relevant, I'm currently working in the confines of creating a shiny section of an R markdown document. Hence the renderPlot wrapper around grid.arrange.

The following is similar to the code that I currently have.

testdata = data.frame(var1 = seq(0,10,by=1), var2 = runif(11), 
var3 = runif(11, min = 100, max = 500))

renderPlot({grid.arrange(
ggplot(data = testdata, aes(x = var1, y = var2))
+ geom_line(colour = "blue") + xlab(NULL),
ggplot(data = testdata, aes(x = var1, y = var3)) + geom_line(colour = "red"))})

Does anyone have any suggestions about how to create the shared legend? Thanks very much for your help.

回答1:

using ggplot2 I usually use the following 2 methods to create a common legend:

Method 1 : When scales are similar

By using facet_grid or just the color parameter in combination with reshape2 package, you can easily combine multiple plots with same legend. But this is ideal in case the values in your variables have a similar magnitude order.

Using color & reshape2:

library('reshape2')
data_melt<-melt(data=testdata,value.name='Value',id.vars='var1')

ggplot(data_melt)+
  geom_line(aes(x=var1,y=Value,color=variable))

Using color, facet_grid & reshape2:

library('reshape2')
data_melt<-melt(data=testdata,value.name='Value',id.vars='var1')

ggplot(data_melt)+
 geom_line(aes(x=var1,y=Value,color=variable))+
 facet_grid(~variable)

Method 2: When scales differ wildly

As you can see,the final plot is great! All you need is to create a plot having your legend & pass it as an input parameter to the custom function created in the wiki here.

testdata = data.frame(var1 = seq(0,10,by=1), var2 = runif(11), 
                  var3 = runif(11, min = 100, max = 500))

library('reshape2')
data_melt<-melt(data=testdata,value.name='Value',id.vars='var1')

p1=ggplot(data = testdata)+
  geom_line(aes(x = var1, y = var2,color='blue'))

p2=ggplot(data = testdata) + 
  geom_line(aes(x = var1, y = var3,color='red'))

p3=ggplot(data_melt)+
   geom_line(aes(x=var1,y=Value,color=variable))

grid.arrange(p1,p2,nrow=2,main='Line Plots')

g_legend<-function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}

legend <- g_legend(p3)
lwidth <- sum(legend$width)

## using grid.arrange for convenience
## could also manually push viewports
grid.arrange(arrangeGrob(p1 + theme(legend.position="none"),
                         p2 + theme(legend.position="none"),
                         main ="Variable Name",
                         left = "Value"),
             legend, 
             widths=unit.c(unit(1, "npc") - lwidth, lwidth), nrow=1)