Lines (ggplot2)

Problem

You want to do add lines to a plot.

Solution

With one continuous and one categorical axis

  1. # Some sample data
  2. dat <- read.table(header=TRUE, text='
  3. cond result
  4. control 10
  5. treatment 11.5
  6. ')
  7. library(ggplot2)

Lines that go all the way across

These use geom_hline because the y-axis is the continuous one, but it is also possible to use geom_vline (with xintercept) if the x-axis is continuous.

  1. # Basic bar plot
  2. bp <- ggplot(dat, aes(x=cond, y=result)) +
  3. geom_bar(position=position_dodge(), stat="identity")
  4. bp
  5. # Add a horizontal line
  6. bp + geom_hline(aes(yintercept=12))
  7. # Make the line red and dashed
  8. bp + geom_hline(aes(yintercept=12), colour="#990000", linetype="dashed")

plot of chunk unnamed-chunk-3plot of chunk unnamed-chunk-3plot of chunk unnamed-chunk-3

Separate lines for each categorical value

To make separate lines for each bar, use geom_errorbar. The error bars have no height – ymin=ymax. It also seems necessary to specify y for some reason, even though it doesn’t do anything.

  1. # Draw separate hlines for each bar. First add another column to dat
  2. dat$hline <- c(9,12)
  3. dat
  4. #> cond result hline
  5. #> 1 control 10.0 9
  6. #> 2 treatment 11.5 12
  7. # Need to re-specify bp, because the data has changed
  8. bp <- ggplot(dat, aes(x=cond, y=result)) +
  9. geom_bar(position=position_dodge(), stat="identity")
  10. # Draw with separate lines for each bar
  11. bp + geom_errorbar(aes(ymax=hline, ymin=hline), colour="#AA0000")
  12. # Make the lines narrower
  13. bp + geom_errorbar(width=0.5, aes(ymax=hline, ymin=hline), colour="#AA0000")
  14. # Can get the same result, even if we get the hline values from a second data frame
  15. # Define data frame with hline
  16. dat_hlines <- data.frame(cond=c("control","treatment"), hline=c(9,12))
  17. dat_hlines
  18. #> cond hline
  19. #> 1 control 9
  20. #> 2 treatment 12
  21. # The bars are from dat, but the lines are from dat_hlines
  22. bp + geom_errorbar(data=dat_hlines, aes(y=NULL, ymax=hline, ymin=hline), colour="#AA0000")
  23. #> Warning: Ignoring unknown aesthetics: y

plot of chunk unnamed-chunk-4plot of chunk unnamed-chunk-4plot of chunk unnamed-chunk-4

Lines over grouped bars

It is possible to add lines over grouped bars. In this example, there are actually four lines (one for each entry for hline), but it looks like two, because they are drawn on top of each other. I don’t think it’s possible to avoid this, but it doesn’t cause any problems.

  1. dat <- read.table(header=TRUE, text='
  2. cond group result hline
  3. control A 10 9
  4. treatment A 11.5 12
  5. control B 12 9
  6. treatment B 14 12
  7. ')
  8. dat
  9. #> cond group result hline
  10. #> 1 control A 10.0 9
  11. #> 2 treatment A 11.5 12
  12. #> 3 control B 12.0 9
  13. #> 4 treatment B 14.0 12
  14. # Define basic bar plot
  15. bp <- ggplot(dat, aes(x=cond, y=result, fill=group)) +
  16. geom_bar(position=position_dodge(), stat="identity")
  17. bp
  18. # The error bars get plotted over one another -- there are four but it looks
  19. # like two
  20. bp + geom_errorbar(aes(ymax=hline, ymin=hline), linetype="dashed")

plot of chunk unnamed-chunk-5plot of chunk unnamed-chunk-5

Lines over individual grouped bars

It is also possible to have lines over each individual bar, even when grouped.

  1. dat <- read.table(header=TRUE, text='
  2. cond group result hline
  3. control A 10 11
  4. treatment A 11.5 12
  5. control B 12 12.5
  6. treatment B 14 15
  7. ')
  8. # Define basic bar plot
  9. bp <- ggplot(dat, aes(x=cond, y=result, fill=group)) +
  10. geom_bar(position=position_dodge(), stat="identity")
  11. bp
  12. bp + geom_errorbar(aes(ymax=hline, ymin=hline), linetype="dashed",
  13. position=position_dodge())

plot of chunk unnamed-chunk-6plot of chunk unnamed-chunk-6

With two continuous axes

Sample data used here:

  1. dat <- read.table(header=TRUE, text='
  2. cond xval yval
  3. control 11.5 10.8
  4. control 9.3 12.9
  5. control 8.0 9.9
  6. control 11.5 10.1
  7. control 8.6 8.3
  8. control 9.9 9.5
  9. control 8.8 8.7
  10. control 11.7 10.1
  11. control 9.7 9.3
  12. control 9.8 12.0
  13. treatment 10.4 10.6
  14. treatment 12.1 8.6
  15. treatment 11.2 11.0
  16. treatment 10.0 8.8
  17. treatment 12.9 9.5
  18. treatment 9.1 10.0
  19. treatment 13.4 9.6
  20. treatment 11.6 9.8
  21. treatment 11.5 9.8
  22. treatment 12.0 10.6
  23. ')
  24. library(ggplot2)

Basic lines

  1. # The basic scatterplot
  2. sp <- ggplot(dat, aes(x=xval, y=yval, colour=cond)) + geom_point()
  3. # Add a horizontal line
  4. sp + geom_hline(aes(yintercept=10))
  5. # Add a red dashed vertical line
  6. sp + geom_hline(aes(yintercept=10)) +
  7. geom_vline(aes(xintercept=11.5), colour="#BB0000", linetype="dashed")

plot of chunk unnamed-chunk-9plot of chunk unnamed-chunk-9

Drawing lines for the mean

It is also possible to compute a mean value for each subset of data, grouped by some variable. The group means would have to be computed and stored in a separate data frame, and the easiest way to do this is to use the dplyr package. Note that the y range of the line is determined by the data.

  1. library(dplyr)
  2. lines <- dat %>%
  3. group_by(cond) %>%
  4. summarise(
  5. x = mean(xval),
  6. ymin = min(yval),
  7. ymax = max(yval)
  8. )
  9. # Add colored lines for the mean xval of each group
  10. sp + geom_hline(aes(yintercept=10)) +
  11. geom_linerange(aes(x=x, y=NULL, ymin=ymin, ymax=ymax), data=lines)
  12. #> Warning: Ignoring unknown aesthetics: y

plot of chunk unnamed-chunk-10

Using lines with facets

Normally, if you add a line, it will appear in all facets.

  1. # Facet, based on cond
  2. spf <- sp + facet_grid(. ~ cond)
  3. spf
  4. # Draw a horizontal line in all of the facets at the same value
  5. spf + geom_hline(aes(yintercept=10))

plot of chunk unnamed-chunk-11plot of chunk unnamed-chunk-11

If you want the different lines to appear in the different facets, there are two options. One is to create a new data frame with the desired values for the lines. Another option (with more limited control) is to use stat and xintercept in geom_line().

  1. dat_vlines <- data.frame(cond=levels(dat$cond), xval=c(10,11.5))
  2. dat_vlines
  3. #> cond xval
  4. #> 1 control 10.0
  5. #> 2 treatment 11.5
  6. spf + geom_hline(aes(yintercept=10)) +
  7. geom_vline(aes(xintercept=xval), data=dat_vlines,
  8. colour="#990000", linetype="dashed")
  9. spf + geom_hline(aes(yintercept=10)) +
  10. geom_linerange(aes(x=x, y=NULL, ymin=ymin, ymax=ymax), data=lines)
  11. #> Warning: Ignoring unknown aesthetics: y

plot of chunk unnamed-chunk-12plot of chunk unnamed-chunk-12