Search code examples
rggplot2

r - ggplot2 geom_errorbar with solid whisker lines and position dodged


I am trying to create a line plot and have one of the groups use a dotted line type. Unfortunately the whiskers can be hard to see with dotted lines so I am trying to add solid lines for the whiskers.

I tried to follow the suggested solution in this questions.

ggplot2 geom_errorbar with different linetype but with solid whiskers

Unfortunately something weird is happening when also using position dodge.

library(ggplot2)
library(dplyr)

dat <- data.frame(cohort = rep(c("Placebo", "Trt 1", "Trt 2"), times = 3),
                  visit = c(rep("Baseline", times = 3), rep("Time 2", times = 3), rep("Time 3", times = 3)),
                  visitnum = c(rep(1, times = 3), rep(2, times = 3), rep(3, times = 3)),
                  mean = rnorm(9)
                  ) %>% 
  mutate(lower = mean - 1,
         upper = mean + 1)

dodge <- position_dodge2(.25)

dat %>% ggplot(aes(x = visitnum, y = mean, color = cohort, group = cohort, linetype = cohort)) +
  geom_line(position = dodge) +
  geom_point(position = dodge) +
  geom_linerange(aes(ymin = lower, ymax = upper),
                 position = dodge) +
  geom_segment(aes(x = visitnum-.1, xend = visitnum+.1, y = lower, yend = lower), position = dodge, linetype = "solid") +
  geom_segment(aes(x = visitnum-.1, xend = visitnum+.1, y = upper, yend = upper), position = dodge, linetype = "solid") +
  
  scale_linetype_manual(values = c("solid", "solid", "dotted")) +
  
  scale_x_continuous(breaks = unique(dat$visitnum), labels = unique(dat$visit))

The lines created by geom_segment seem to be right adjusted.

plot

Any help would be greatly appreciated.


Solution

  • One option would be to "manually" dodge the whiskers aka segments by computing the x and xend positions yourself instead of relying on position_dodge:

    library(ggplot2)
    
    set.seed(123)
    
    dw <- .45
    dodge <- position_dodge(dw)
    
    dat$cohort_num <- as.numeric(factor(dat$cohort))
    dat |> ggplot(aes(x = visitnum, y = mean, color = cohort, group = cohort, linetype = cohort)) +
      geom_line(position = dodge) +
      geom_point(position = dodge) +
      geom_linerange(aes(ymin = lower, ymax = upper),
        position = dodge
      ) +
      geom_segment(
        aes(
          x = visitnum - .05 + dw / 3 * scales::rescale(cohort_num, to = c(-1, 1)),
          xend = visitnum + .05 + dw / 3 * scales::rescale(cohort_num, to = c(-1, 1)),
          y = lower, yend = lower
        ),
        linetype = "solid"
      ) +
      geom_segment(
        aes(
          x = visitnum - .05 + dw / 3 * scales::rescale(cohort_num, to = c(-1, 1)),
          xend = visitnum + .05 + dw / 3 * scales::rescale(cohort_num, to = c(-1, 1)),
          y = upper, yend = upper
        ),
        linetype = "solid"
      ) +
      scale_linetype_manual(values = c("solid", "solid", "dotted")) +
      scale_x_continuous(breaks = unique(dat$visitnum), labels = unique(dat$visit))