ggplot2: Match plot area height of combined plots

2019-07-29 06:46发布

问题:

library(ggplot2)
library(gridExtra)

df1 <- data.frame(x=c("A1","A2","A3","A4"),something=c(10,18,24,32),col=rep(c("A","B"),2))
df2 <- data.frame(x=c("C1","C2","C3","C4"),somethingelse=c(10543,182334,242334,32255),col=rep(c("A","B"),2))


p1 <- ggplot(df1,aes(x,something,fill=col))+
  ggtitle("Plot")+
  geom_bar(stat="identity")+
  theme(legend.position="top",
        legend.justification="right",
        legend.direction="horizontal")
p2 <- ggplot(df2,aes(x,somethingelse,fill=col))+
  ggtitle("Plot")+
  geom_bar(stat="identity")+
  theme(legend.position="top",
        legend.justification="right",
        legend.direction="horizontal")

I have these two plots that I would like to combine together with equal widths, common title and legend. I thought rather than pulling out the titles and legend grobs, I would leave it on the top plot and hide it in the second plot. This way, I don't have to set heights manually in grid.arrange().

#using loops to generalise to n plots
plist <- list()
plist[[1]] <- p1
plist[[2]] <- p2
grobs <- list()

for (i in 1:length(plist)){
  if(i!=1) plist[[i]] <- plist[[i]]+theme(legend.position="none",plot.title=element_blank())
  grobs[[i]] <- ggplotGrob(plist[[i]])
  widths[[i]] <- grobs[[i]]$widths[2:5]
}

# fix widths
maxwidth <- do.call(grid::unit.pmax, widths)
for (i in 1:length(grobs)){
  grobs[[i]]$widths[2:5] <- as.list(maxwidth)
}

#plot
pgrob = do.call("arrangeGrob",grobs)
grid.arrange(pgrob)

But, here the plot area heights are very different. So I manually set the height of all plots as the first.

for (i in 1:length(grobs)){
  grobs[[i]]$heights[2:5] <- grobs[[1]]$heights[2:5]
}

#plot
pgrob = do.call("arrangeGrob",grobs)
grid.arrange(pgrob)

Now, I end up with this huge blank space above the second plot. How do I get rid of that?

回答1:

You can use ggpubr package to set same legend for both plots (no need to extract grobs and adjust heights).

library(ggpubr)
figure <- ggarrange(p1, p2, nrow = 2, align = "v", common.legend = TRUE)
annotate_figure(figure, fig.lab = "Plot")

Explantion:

  • align = "v" aligns plots vertically
  • common.legend = TRUE sets same legend for both plots
  • annotate_figure adds shared label

Used data:

df1 <- data.frame(x=c("A1","A2","A3","A4"),something=c(10,18,24,32),col=rep(c("A","B"),2))
df2 <- data.frame(x=c("C1","C2","C3","C4"),somethingelse=c(10543,182334,242334,32255),col=rep(c("A","B"),2))

library(ggplot2)
p1 <- ggplot(df1,aes(x,something,fill=col))+
  geom_bar(stat="identity")+
  theme(legend.position="top",
        legend.justification="right",
        legend.direction="horizontal")
p2 <- ggplot(df2,aes(x,somethingelse,fill=col))+
  geom_bar(stat="identity")+
  theme(legend.position="top",
        legend.justification="right",
        legend.direction="horizontal")


回答2:

If you can and restructure and rbind your dataframes, you can use facet_wrap(..., scale = "free") to get what you want.

library(ggplot2)
library(gridExtra)

df1 <- data.frame(x=c("A1","A2","A3","A4"),y=c(10,18,24,32),col=rep(c("A","B"),2), type = "something")
df2 <- data.frame(x=c("C1","C2","C3","C4"),y=c(10543,182334,242334,32255),col=rep(c("A","B"),2), type = "somethingelse")


df <- rbind(df1, df2)

ggplot(df,aes(x,y,fill=col))+
    ggtitle("Plot")+
    geom_bar(stat="identity")+
    facet_wrap(~type, ncol = 1, scales = "free") +
    theme(legend.position="top",
          legend.justification="right",
          legend.direction="horizontal")


标签: r ggplot2