Search code examples
rggplot2patchwork

Is it possible to create overlapping y-axes with a shared x-axis?


I often have figures that have the same shared x-axis but different y-axes. They cannot be faceted as some of the y-axes need complex names and/or have flipped coordinates for historical/interpretational reasons.

Below I plot an example of a fake record like this, and show how I would like this plot to look like after tweaking it in Inkscape. Is there any way to achieve this result from within R/patchwork?

library(tidyverse)
library(patchwork)

set.seed(123)
dat <- tibble::tibble(a = 1:10,
                      b = c(0, 2, 5, 8, 5, 4, 6, 4, 3, 2),
                      c = -stats::rnorm(10, b, 3),
                      d = sample(letters[1:3], 10, TRUE))

shared_x <- c(0, 11)

pl1 <- dat %>%
  ggplot(aes(x = a, y = b)) +
  geom_point(colour = "indianred") +
  coord_cartesian(xlim = shared_x) +
  scale_y_continuous("B is awesome", position = "right")

pl2 <- dat %>%
  ggplot(aes(x = a, y = c)) +
  geom_point(colour = "steelblue") +
  scale_y_reverse("C is inversely related to B") +
  coord_cartesian(xlim = shared_x, ylim = c(3, -13))

theme_set(theme_classic())
(pl1 & theme(axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(), axis.line.x = element_blank())) / pl2

ggsave("overlapping_axes.pdf", width = 10, height = 8, units = "cm")

Created on 2021-07-09 by the reprex package (v2.0.0)

and here's the manually tweaked version:


Solution

  • I see that r2evans already has given you the warnings that should be considered everytime one might think a secondary axis would solve your problems. I agree with them, however, that doesn't mean that it can't be done.

    As long as you can think of a transformation and it's inverse you could probably do this with secondary axes (not to be confused with 'you should do this'). The truncated look of the axis can be replicated with ggh4x::axis_truncated() (disclaimer, I'm the author of that function).

    In the example below the transformation for the secondary axis -x * 0.5 so the inverse is - x / 0.5, but you can probably these better to your needs.

    library(ggplot2)
    library(ggh4x)
    
    set.seed(123)
    dat <- tibble::tibble(a = 1:10,
                          b = c(0, 2, 5, 8, 5, 4, 6, 4, 3, 2),
                          c = -stats::rnorm(10, b, 3),
                          d = sample(letters[1:3], 10, TRUE))
    
    ggplot(dat, aes(a)) +
      geom_point(aes(y = b, colour = "b")) +
      geom_point(aes(y = -c * 0.5, colour = "c")) +
      scale_y_continuous(
        position = "right", breaks = seq(0, 8, by = 2),
        guide = guide_axis_truncated(),
        name = "B is awesome",
        sec.axis = sec_axis(
          trans = ~ -.x/0.5, breaks = c(0, -5, -10),
          guide = guide_axis_truncated(trunc_lower = -Inf),
          name = "C is inversely related to B"
        )
      ) +
      scale_colour_manual(values = c("indianred", "steelblue")) +
      theme_classic() +
      theme(
        axis.title.y.left = element_text(colour = "steelblue"),
        axis.title.y.right = element_text(colour = "indianred")
      )
    

    Created on 2021-07-09 by the reprex package (v1.0.0)

    You can probably reposition the axis titles a little bit better by setting appropriate hjusts.