Search code examples
rggplot2facetaxesfacet-wrap

Ggplot2 facets: put y-axis of the right hand side panel on the right side


I'm trying to make a facetted plot with two panels - left and right. The variable on the x-axis is continuous, and the variable on the y-axis is discrete, with relatively long labels. I would like to put the y-axis of the right hand side plot on the right side (and keep the y-axis of the left hand side left), so that the two panels are not split apart by the y-axis labels of the right hand side plot.

I've tried a couple of different workaround (e.g. cowplot) but I can't get anything that I would be satisfied with, because I also need a legend in my plot.

This is what I want to do: enter image description here

Here's a reprex:

library(tidyverse)

region <- sample(words, 20)
panel <- rep(c(0, 1), each = 10)
value <- rnorm(20, 0, 1)

df <- tibble(region, panel, value)

ggplot(df, aes(value, region)) +
  geom_point() +
  facet_wrap(~ panel, scales = 'free_y')

Thank you!


Solution

  • This solution lacks flexibility for cases with more than 2 plots but it does the job for your case. The idea is to generate the plots separately and combine the plots into a list. The ggplot function call contains an if else function for the scale_y_discrete layer which puts the y-axis either on the left-hand or right-hand side depending on the value of panel. We use gridExtra to combine the plots.

    library(tidyverse)
    library(gridExtra)
    
    region <- sample(words, 20)
    panel <- rep(c(0, 1), each = 10)
    value <- rnorm(20, 0, 1)
    
    df <- tibble(region, panel, value)
    
    panel <- sort(unique(df$panel))
    plot_list <- lapply(panel, function(x) {
      ggplot(data = df %>% filter(panel == x), 
             aes(value, region)) +
             geom_point() +
             if (x == 0) scale_y_discrete(position = "left") else scale_y_discrete(position = "right")
    })
    
    do.call("grid.arrange", c(plot_list, ncol = 2))
    

    You can leave the facet_wrap(~ panel, scales = 'free_y') layer and you will retain the strips at the top in the plot.

    enter image description here

    UPDATE

    Code updated to remove x-axis from the individual plots and add text at the bottom location of the grid plot; added a second if else to suppress the y-axis title in the right hand plot. Note that the if else functions need to be enclosed by curly brackets (did not know that either :-) but it makes sense):

    plot_list <- lapply(panel, function(x) {
      ggplot(data = df %>% filter(panel == x), aes(x = value, y = region)) +
             geom_point() +
             theme(axis.title.x = element_blank()) +
             facet_wrap(. ~ panel) +
             {if (x == 0) scale_y_discrete(position = "left") else scale_y_discrete(position = "right")} +
             {if (x == 1) theme(axis.title.y = element_blank())}
    })
    
    do.call("grid.arrange", c(plot_list, ncol = 2, bottom = "value"))
    

    enter image description here