How can I make a discontinuous axis in R with ggplot2?

You could make use of the extended axis guides in the ggh4x package. Alas, you won’t easily be able to create the “separators” without a hack similar to the one suggested by user Zhiqiang Wang

guide_axis_truncated accepts vectors to define lower and upper trunks. This also works for units, by the way, then you have to pass the vector inside the unit function (e.g., trunc_lower = unit(c(0,.45), "npc") !

library(ggplot2)
library(ggh4x)

set.seed(321)
dat <- data.frame(matrix(ncol = 2, nrow = 18))
x <- c("Month", "Value")
colnames(dat) <- x
dat$Month <- rep(c(1,2,3,10,11,12),3)
dat$Value <- rnorm(18,20,2)

# this is to make it slightly more programmatic
x1end <- 3.45
x2start <- 3.55

p <-
ggplot(data = dat, aes(x = factor(Month), y = Value)) +
  geom_boxplot() +
  labs(x = "Month") +
  theme_classic() +
  theme(axis.line = element_line(colour = "black"))

p +
  guides(x = guide_axis_truncated(
    trunc_lower = c(-Inf, x2start),
    trunc_upper = c(x1end, Inf)
  ))

Created on 2021-11-01 by the reprex package (v2.0.1)

The below is taking user Zhiqiang Wang’s hack a step further. You will see I am using simple trigonometry to calculate the segment coordinates. in order to make the angle actually look as it is defined in the function, you would need to set coord_equal.

# a simple function to help make the segments
add_separators <- function(x, y = 0, angle = 45, length = .1){
  add_y <-  length * sin(angle * pi/180)
  add_x <- length * cos(angle * pi/180)
  ## making the list for your segments
  myseg <- list(x = x - add_x, xend = x + add_x, 
                y = rep(y - add_y, length(x)), yend = rep(y + add_y, length(x)))
  ## this function returns an annotate layer with your segment coordinates
  annotate("segment", 
           x = myseg$x, xend = myseg$xend,
           y = myseg$y, yend = myseg$yend) 
}

# you will need to set limits for correct positioning of your separators
# I chose 0.05 because this is the expand factor by default 
y_sep <- min(dat$Value) -0.05*(min(dat$Value))

p +
  guides(x = guide_axis_truncated(
    trunc_lower = c(-Inf, x2start),
    trunc_upper = c(x1end, Inf)
  )) +
  add_separators(x = c(x1end, x2start), y = y_sep, angle = 70) +
  # you need to set expand to 0
  scale_y_continuous(expand = c(0,0)) +
  ## to make the angle look like specified, you would need to use coord_equal()
  coord_cartesian(clip = "off", ylim = c(y_sep, NA)) 

Leave a Comment