Search code examples
rggplot2r-grid

Two separate y-axis titles for two facets of a plot while keeping the facet top strip labels using ggplot2


I have this data frame:

> str(DF)
'data.frame':   14084 obs. of  6 variables:
.
.
.
 $ Variables: chr  "Height" "Height" "Height" "Height" ...
 $ Values   : num  245 129 301 162 123 125 115 47 46 135 ...
 $ Year     : Factor w/ 2 levels "2015","2016": 1 1 1 1 1 1 1 1 1 1 ...

I wrapped the plot in two facets (in two columns) using facet_wrap() with free axes scales.

ggplot(data = DF, aes(x = Year, y = Values)) +
  geom_boxplot() +
  facet_wrap("Variables", scales = "free")

My problem is:
Both the facets share a common y-axis title. What I would like, however, is two separate y-axis titles for the two facets. A common x-axis title is fine for me.

I came across this question Showing different axis labels using ggplot2 with facet_wrap but it does not solve the problem the way I want as I do not want to lose the facet labels on top. Moreover, my facets are arranged horizontally.

I tried using labs():

. . . +
labs(y=c("A", "B"))

But it just changes the y-axis title of first facet to A and the second facet remains as it is.

How could I address this problem?

Any help would be appreciated.


Solution

  • Not sure if I fully understood what you need, but here is my attempt based on your suggested link and this one.

    library(ggplot2)
    library(grid)
    library(gtable)
    
    # Some fake data
    set.seed(2018)
    df <- data.frame(variables = rep(c("var1", "var2"), times = 100),
                     values = rnorm(100),
                     year = as.factor(2001:2010))
    
    # Make a plot with positioning the strip labels to the right of each facet,
    # acting as OY axis titles.
    # We will add the top strip labels in the next step,
    # using the `grid` & `gtable` packages functionality.
    p <- ggplot(df, aes(x = year, y = values, group = year) ) +
      geom_boxplot() +
      facet_wrap(~ variables, scales = "free_y", nrow = 1, 
                 strip.position = "left", 
                 labeller = as_labeller(c(var1 = "Example var1", 
                                          var2 = "Example var2") ) )  +
      ylab(NULL) +
      theme(strip.background = element_blank(),
            strip.placement = "outside",
            # Customize theme so that is black & white style as requested
            panel.background = element_rect(fill = NA, colour = 'black'),
            panel.grid = element_blank())
    p
    

    enter image description here

    Add the top strip labels using the functionality of the grid & gtable packages:

    # Get gtable object
    g <- ggplotGrob(p)
    # Sometimes helpful to get an idea about the grobs' position
    grid.show.layout(gtable:::gtable_layout(g)) 
    
    # Add an extra top row (make some space)
    g <- gtable_add_rows(x = g, heights = unit(0.65, 'cm'), pos = 2)
    # First strip
    g <- gtable_add_grob(x = g,
                         grobs = list(rectGrob(gp = gpar(col = NA, 
                                                         fill = NA)),
                                      textGrob(label = "var1", 
                                               gp = gpar(col = "black"))),
                         t = 3, l = 7, b = 3, r = 7, 
                         name = c("strip-top-1-rectg", "strip-top-1-text"))
    # Second strip
    g <- gtable_add_grob(x = g,
                         grobs = list(rectGrob(gp = gpar(col = NA, 
                                                         fill = NA)),
                                      textGrob(label = "var2", 
                                               gp = gpar(col = "black"))),
                         t = 3, l = 13, b = 3, r = 13, 
                         name = c("strip-top-2-rectg", "strip-top-2-text")) 
    
    # Draw the edited plot
    grid.newpage()
    grid.draw(g)
    

    enter image description here