Millionhorns
Millionhorns

Reputation: 95

Creating error bars in one direction, a special case

Long time lurker here.

I am plotting the predicted values from a previously run, nlme model using the output by ggpredict. I have built a line plot and want to create error bars on the points. Though, reviewers want the error bars to only be going one direction (i.e., for the group who is higher = positive error bars, while the group who is negative gets negative error bars). I've successfully done this with this code:

Q1 <- ggpredict(MODEL, c("day", "group"), 
               #ci.lvl = NA,
               type = "re")

q2 <- Q1 %>% 
  mutate(is.int = group == "Intervention",
         conf.high = ifelse(is.int, std.error, conf.high),
         conf.low = ifelse(is.int, 0, conf.low)) %>% 
  select(-is.int) %>% 
  mutate(is.cntr = group == "Control",
         conf.high = ifelse(is.cntr, 0, conf.high),
         conf.low = ifelse(is.cntr, std.error, conf.low)) %>% 
  select(-is.cntr)


    ggplot(data = q2,
           aes(x, predicted, group = group)) +
      geom_point(aes(shape = group), size = 6) +
      scale_shape_manual(values=c(19, 1)) +
      geom_line(size = 2,
                aes(linetype = group),
                color = "black") +
      scale_linetype_manual(values = c("solid", "dashed")) +
      geom_linerange(size = 1,
                     aes(ymin = predicted - conf.low,
                         ymax = predicted + conf.high),
                     color = "black",
                     alpha = .8) +
      labs(x = "Time",
           y = "",
           linetype = "",
           shape = "") 
      scale_y_continuous(limits =c(1500, 2000)) +
      geom_bracket(
        xmin = "PRE", xmax = c("MID1", "MID2", "MID3", "POST"),
        y.position = c(1850,1900,1950,2000), tip.length = 0.05,
        label = c("*", "**", "***", "#"), size = 1)

and it works well.

however

The reviewers would like "caps" on the end of the error bars that i've reated with geom_linerange(). Obviously, geom_errorbar() is almost purpose built to have the "caps". But, when I use it, it creates lines across my data. See pic attached for example. example of bad error bars

Thoughts??

Edit, Reproducibile data:

    dput(q2)
structure(list(x = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 4L, 4L, 
5L, 5L), .Label = c("PRE", "MID1", "MID2", "MID3", "POST"), class = "factor"), 
    predicted = c(1666.97185871754, 1660.27445165342, 1743.2831065274, 
    1678.48945165342, 1788.50605542978, 1637.40907049806, 1807.55826371403, 
    1639.78265640012, 1865.8766220711, 1652.91070173056), std.error = c(88.8033117577884, 
    91.257045996107, 92.9973963841595, 95.3834973421298, 95.0283457128716, 
    97.3739053806999, 95.6466346849776, 97.9142418717957, 93.3512943191676, 
    95.5735155125126), conf.low = c(0, 91.257045996107, 0, 95.3834973421298, 
    0, 97.3739053806999, 0, 97.9142418717957, 0, 95.5735155125126
    ), conf.high = c(88.8033117577884, 0, 92.9973963841595, 0, 
    95.0283457128716, 0, 95.6466346849776, 0, 93.3512943191676, 
    0), group = structure(c(1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 
    2L), .Label = c("Intervention", "Control"), class = "factor")), class = "data.frame", row.names = c(NA, 
-10L))

Upvotes: 1

Views: 490

Answers (1)

teunbrand
teunbrand

Reputation: 37903

My suggestion is to highjack the arrow argument of geom_segment() to serve as caps, since you've already precomputed the confidence intervals. You can control with an ifelse whether to take the lower or higher confidence level. Example with dummy data below:

library(ggplot2)

# Make dummy data
df <- data.frame(
  x = c(1:10, 1:10),
  y = c(1:10, seq(0.5, 5, by = 0.5)),
  type = rep(LETTERS[1:2], each = 10)
)

# Dummy confidence intervals
df$conf.hi <- df$y * 1.1
df$conf.lo <- df$y * 0.9

ggplot(df, aes(x, y, colour = type)) +
  geom_line() +
  geom_point() +
  geom_segment(aes(xend = x, 
                   yend = ifelse(type == "A", conf.hi, conf.lo)),
               arrow = arrow(angle = 90))

Created on 2020-04-03 by the reprex package (v0.3.0)

EDIT: Example for added data

Probably, the reason that it didn't turn out as the example was because the conf.high/conf.low variables were not added to the prediction. I used the std.error instead of the conf.* variables, as I noticed these are similar values. I got a decent plot using the following.

ggplot(q2, aes(x, predicted, colour = group)) +
  geom_line(aes(group = group)) +
  geom_point() +
  geom_segment(aes(xend = x, 
                   yend = predicted + ifelse(group == "Intervention", std.error, -std.error)),
               arrow = arrow(angle = 90))

enter image description here

Upvotes: 1

Related Questions