ggplot: how to add common x and y labels to a grid of plots

后端 未结 1 1667
小蘑菇
小蘑菇 2020-12-19 14:09

Using diamonds, I want to plot carat vs price for 4 levels (Fair, Good, Very Good and Pre

相关标签:
1条回答
  • 2020-12-19 14:51

    In addition to using functions from the gridExtra package (as suggested by @user20650), you can also create your plots with less code by splitting the diamonds data frame by levels of cut and using mapply.

    The answer below also includes solutions for follow-up questions in the comments. We show how to lay out the four plots, add single x and y labels (including making them bold and controlling their color and size) that apply to all the plots, and get a single legend rather than a separate legend for each plot.

    library(ggplot2)
    library(gridExtra)
    library(grid)
    library(scales)
    

    Remove rows where cut is "Ideal":

    dat = diamonds[diamonds$cut != "Ideal",]
    dat$cut = droplevels(dat$cut)
    

    Create four plots, one for each remaining level of cut and store in a list. We use mapply (instead of lapply) so that we can provide both separate data frames for each level of cut and a vector of custom ymax values to set the highest value on the y-axis separately for each plot. We also add color=clarity in order to create a color legend:

    pl = mapply(FUN = function(df, ymax) {
      ggplot(df, aes(carat, price, color=clarity))+
        geom_point()+
        facet_wrap(~cut, ncol=2)+
        scale_x_continuous(limits = c(0,4), breaks=0:4)+
        scale_y_continuous(limits = c(0, ymax), labels=dollar_format()) +
        labs(x=expression(" "),
             y=expression(" "))
    }, df=split(dat, dat$cut), ymax=c(1e4,5e3,1e3,3e3), SIMPLIFY=FALSE)
    

    Okay, we have our four plots, but each one has its own legend. So now we want to arrange to have only one overall legend. We do this by extracting one of the legends as a separate grob (graphical object) and then removing the legends from the four plots.

    Extract the legend as a separate grob using a small helper function:

    # Function to extract legend
    # https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
    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) }
    
    # Extract legend as a grob
    leg = g_legend(pl[[1]])
    

    Now we need to arrange the four plots in a 2x2 grid and then place the legend to the right of this grid. We use arrangeGrob to lay out the plots (and note how we use lapply to remove the legend from each plot before rendering it). This is essentially the same as what we did with grid.arrange in an earlier version of this answer, except that arrangeGrob creates the 2x2 plot grid object without drawing it. Then we lay out the legend beside the 2x2 plot grid by wrapping the whole thing inside grid.arrange. widths=c(9,1) allocates 90% of the horizontal space to the 2x2 grid of plots and 10% to the legend. Whew!

    grid.arrange(
      arrangeGrob(grobs=lapply(pl, function(p) p + guides(colour=FALSE)), ncol=2, 
                  bottom=textGrob("Carat", gp=gpar(fontface="bold", col="red", fontsize=15)), 
                  left=textGrob("Price", gp=gpar(fontface="bold", col="blue", fontsize=15), rot=90)),
      leg, 
      widths=c(9,1)
    )
    

    0 讨论(0)
提交回复
热议问题