tassones
tassones

Reputation: 1692

How can I draw line segment across plot axis in ggplot2?

I need to draw line segments across (and on) the x-axis boundary of a ggplot2 figure so that I can make axis breaks. This SO question is similar but does not have an answer for how to implement multiple axis breaks and the top answer is rather cumbersome.

Example Data

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

This is the basic figure, note the theme is theme_bw(). I would like to keep this theme so that this figure resembles others. In the similar SO question, the top answer does not use theme_bw().

library(ggplot2)

ggplot(data = dat, aes(x = factor(Month), y = Value)) +
  geom_boxplot() +
  labs(x = "Month") +
  scale_y_continuous(breaks = seq(15,24,1),
                     limits = c(15,24)) +
  theme_bw() +
  theme(panel.grid = element_blank(),
        text = element_text(size = 16),
        axis.text.x = element_text(size = 14, color = "black"),
        axis.text.y = element_text(size = 14, color = "black"))

This is as far as I got because I could not find a way to extend the geom_segment() across the x-axis boundary.

ggplot(data = dat, aes(x = factor(Month), y = Value)) +
  geom_boxplot() +
  labs(x = "Month") +
  geom_segment(aes(x = 2.45, xend = 2.45,
                   y = -Inf, yend = 15)) +
  geom_segment(aes(x = 2.55, xend = 2.55,
                   y = -Inf, yend = 15)) +
  geom_segment(aes(x = 4.45, xend = 4.45,
                   y = -Inf, yend = 15)) +
  geom_segment(aes(x = 4.55, xend = 4.55,
                   y = -Inf, yend = 15)) +
  scale_y_continuous(breaks = seq(15,24,1),
                     limits = c(15,24)) +
  theme_bw() +
  theme(panel.grid = element_blank(),
        text = element_text(size = 16),
        axis.text.x = element_text(size = 14, color = "black"),
        axis.text.y = element_text(size = 14, color = "black"))

The ideal figure would look like the figure below, which I created 'by-hand' in MS Word which I am trying to avoid. enter image description here

It would be great if ggbreak had a way to do this but it currently does not as far as I am aware.

Upvotes: 0

Views: 1428

Answers (2)

tjebo
tjebo

Reputation: 23737

The answer in the linked thread is not cumbersome and it works really well for this problem.

library(ggh4x)
#> Loading required package: ggplot2
x_end <- c(2.45, 4.45)
x_start <- x_end + 0.1

ggplot(data = dat, aes(x = factor(Month), y = Value)) +
  geom_boxplot() +
  labs(x = "Month") +
  theme_classic() +
  guides(x = guide_axis_truncated(
    trunc_lower = c(-Inf, x_start),
    trunc_upper = c(x_end, Inf)
  )) +
  ## add the segments simply with text annotation - as suggested by teunbrand in the comment in the linked thread
  annotate("text", y = -Inf, x = c(x_end, x_start), label = "I") + 
  coord_cartesian(clip = "off")

Upvotes: 3

Allan Cameron
Allan Cameron

Reputation: 173793

You need to turn off the limits off your y scale. Use the ylim argument in coord_cartesian instead, and set clip = 'off'.

To get the look you are going for, it's probably easiest to add the geom layers as an annotation.

Note also that the annotations are drawn underneath the panel border, so you need to turn that off. In the example below I have drawn it back in using annotation_custom so that the subsequent annotations are drawn over it.

library(ggplot2)

ggplot(data = dat, aes(x = factor(Month), y = Value)) +
  geom_boxplot() +
  labs(x = "Month") +
  scale_y_continuous(breaks = seq(15,24,1)) +
  theme_bw() +
  theme(panel.grid = element_blank(),
        text = element_text(size = 16),
        axis.text.x = element_text(size = 14, color = "black"),
        axis.text.y = element_text(size = 14, color = "black"),
        panel.border = element_blank()) +
  coord_cartesian(clip = 'off', ylim = c(15, 24.5)) +
  annotation_custom(grid::rectGrob(gp = grid::gpar(fill = NA))) +
  annotate('rect', xmin = c(2.33, 4.33), xmax = c(2.67, 4.67),
           ymin = 14, ymax = 16, fill = 'white') +
  annotate('segment', x = c(2.33, 2.67, 4.33, 4.67),
           xend = c(2.33, 2.67, 4.33, 4.67), y = 14.2, yend = 15)

enter image description here

Upvotes: 4

Related Questions