Search code examples
rggplot2figurefacet-wrapyaxis

Dual y-axis while using facet_wrap in ggplot with varying y-axis scale


I am trying to plot several panels in ggplot2 while using facet_wrap. I want to have two y-axis. Left side y-axis ranged from 5 to 27 and right side y-axis ranged from 25 to 27. I want to show all the data points with regression line. However, when I am plotting, the variation in the right side y-axis is lower comparing to other dataset so appearing as a flat line. I want to keep right side y-axis from 25 to 27 so that variation in the data can be seem clearly. I used this code 1 but not able to sort it out. Any help is highly appreciated.

library(ggplot2)

scaleFactor <- max(d1$weeks) / max(d1$income)

ggplot(mtcars, aes(x=Year)) +
  geom_smooth(aes(y=weeks), method="loess", col="blue") +
  geom_smooth(aes(y=income * scaleFactor), method="loess", col="red") +
  scale_y_continuous(name="weeks", sec.axis=sec_axis(~./scaleFactor, name="income")) +
  theme(
    axis.title.y.left=element_text(color="blue"),
    axis.text.y.left=element_text(color="blue"),
    axis.title.y.right=element_text(color="red"),
    axis.text.y.right=element_text(color="red")
  )

Blockquote


Solution

  • If this is about making the ranges of the data overlap instead of just rescaling the maximum, you can try the following.

    First we'll make function factory to make our job easier:

    library(ggplot2)
    library(scales)
    #> Warning: package 'scales' was built under R version 4.0.3
    
    # Function factory for secondary axis transforms
    train_sec <- function(from, to) {
      from <- range(from)
      to   <- range(to)
      # Forward transform for the data
      forward <- function(x) {
        rescale(x, from = from, to = to)
      }
      # Reverse transform for the secondary axis
      reverse <- function(x) {
        rescale(x, from = to, to = from)
      }
      list(fwd = forward, rev = reverse)
    }
    

    Then, we can use the function factory to make transformation functions for the data and for the secondary axis.

    # Learn the `from` and `to` parameters
    sec <- train_sec(mtcars$hp, mtcars$cyl)
    

    Which you can apply like this:

    ggplot(mtcars, aes(x=disp)) +
      geom_smooth(aes(y=cyl), method="loess", col="blue") +
      geom_smooth(aes(y= sec$fwd(hp)), method="loess", col="red") +
      scale_y_continuous(name="cyl", sec.axis=sec_axis(~sec$rev(.), name="hp")) +
      theme(
        axis.title.y.left=element_text(color="blue"),
        axis.text.y.left=element_text(color="blue"),
        axis.title.y.right=element_text(color="red"),
        axis.text.y.right=element_text(color="red")
      )
    #> `geom_smooth()` using formula 'y ~ x'
    #> `geom_smooth()` using formula 'y ~ x'
    

    Here is an example with a different dataset.

    sec <- train_sec(economics$psavert, economics$unemploy)
    
    ggplot(economics, aes(date)) +
      geom_line(aes(y = unemploy), colour = "blue") +
      geom_line(aes(y = sec$fwd(psavert)), colour = "red") +
      scale_y_continuous(sec.axis = sec_axis(~sec$rev(.), name = "psavert"))
    

    Created on 2021-02-04 by the reprex package (v1.0.0)