Add Text Annotations to ggplot2 Faceted Plot


In my experience with R learners there are two basic types. The “show me the code and what it does and let me play” type and the “please give me step by step directions” type. I’ve broken the following tutorial on plotting text on faceted ggplot2 plots into 2 sections:

  1. The Complete Code and Final Outcome
  2. A Bit of Explanation

Hopefully, whatever learner you are you’ll be plotting text on faceted graphics in no time.


Section 1: The Complete Code and Final Outcome

mtcars[, c("cyl", "am", "gear")] <- lapply(mtcars[, c("cyl", "am", "gear")], as.factor)

p <- ggplot(mtcars, aes(mpg, wt, group = cyl)) + 
    geom_line(aes(color=cyl)) +
    geom_point(aes(shape=cyl)) + 
    facet_grid(gear ~ am) +
    theme_bw()                                                                      
p                                                                     
 

len <- length(levels(mtcars$gear)) *  length(levels(mtcars$am))

vars <- data.frame(expand.grid(levels(mtcars$gear), levels(mtcars$am)))
colnames(vars) <- c("gear", "am")
dat <- data.frame(x = rep(15, len), y = rep(5, len), vars, labs=LETTERS[1:len])

p + geom_text(aes(x, y, label=labs, group=NULL),data=dat) 


dat[1, 1:2] <- c(30, 2)   #to change specific locations
p + geom_text(aes(x, y, label=labs, group=NULL), data=dat) 


p + geom_text(aes(x, y, label=paste("beta ==", labs), group=NULL), size = 4, 
    color = "grey50", data=dat, parse = T) 

final


Section 2: A Bit of Explanation

The following portion of the tutorial provides a bit more of a step by step procedure for plotting text to faceted plots as well as a visual to go with the code.

The initial non annotated plot
First, let’s make a faceted line plot with the mtcars data set. I reclassed a few variables to make factors.

mtcars[, c("cyl", "am", "gear")] <- lapply(mtcars[, c("cyl", "am", "gear")], as.factor)

p <- ggplot(mtcars, aes(mpg, wt, group = cyl)) + 
    geom_line(aes(color=cyl)) +
    geom_point(aes(shape=cyl)) + 
    facet_grid(gear ~ am) +
    theme_bw()                                                                      
p                                                                     

initial
Add text to each facet
The key here is a new data frame with three pieces of information (ggplot2 seems to like information given in a data frame).

  1. Coordinates to plot the text
  2. The faceted variable levels
  3. The labels to be supplied

The first information piece is the coordinates (two columns x and y) to plot the text in each facet. Generally I find that one set of coordinates will work in most of the facet boxes and I just use rep to make these coordinates (I suppose the recycling rule could be used if you added it to an already existing data frame).

The second information piece is the faceted variable labels (in our case gear ~ am). There’re many ways to achieve this but I like a combination of levels and expand.grid. I renamed these columns to be exactly the same as the variable names (gear & am) I used in the original data frame (mtcars in this case).

Lastly, you must make the labels. I chose letters so you can track what piece of the data frame is plotted in which facet.

Your data should look something like this:

   x y gear am labs
1 30 2    3  0    A
2 15 5    4  0    B
3 15 5    5  0    C
4 15 5    3  1    D
5 15 5    4  1    E
6 15 5    5  1    F

Note that the group=NULL is essential to let ggplot2 know you’re dealing with a new data set and the mapping from before can be forgotten (or at least this is how I understand it).

#long cut way to find number of facets
len <- length(levels(mtcars$gear)) *  length(levels(mtcars$am))

vars <- data.frame(expand.grid(levels(mtcars$gear), levels(mtcars$am)))
colnames(vars) <- c("gear", "am")
dat <- data.frame(x = rep(15, len), y = rep(5, len), vars, labs=LETTERS[1:len])

p + geom_text(aes(x, y, label=labs, group=NULL),data=dat)  

second

Moving just one text location
Generally I can usually find one spot that most every text plot will work except that one dog gone facet that just won’t match up with the other coordinates. In this case label A is that pesky label. The key here is to figure out what text labels you want to move and alter those coordinates appropriately.

dat[1, 1:2] <- c(30, 2)   #to change specific locations
p + geom_text(aes(x, y, label=labs, group=NULL), data=dat) 

third
Adding equation (Greek letters/math) and alter size/color
To annotate with math code use the parse = T argument in geom_text. For more on plotting math code see this ggplot wiki and this SO question. To alter the size just throw a size argument in geom_text. I also toned down the color of the text a bit to allow the line to pop the most visually.

p + geom_text(aes(x, y, label=paste("beta ==", labs), group=NULL), size = 4, 
    color = "grey50", data=dat, parse = T)  

final

If you have suggestions for improvement, links, or other thoughts please leave a comment.

Advertisement

About tylerrinker

Data Scientist, open-source developer , #rstats enthusiast, #dataviz geek, and #nlp buff
This entry was posted in annotate, ggplot2 and tagged , , , , , , . Bookmark the permalink.

16 Responses to Add Text Annotations to ggplot2 Faceted Plot

  1. Luca Fenu says:

    Great post, thank you for sharing.

    I’ll keep it in mind in case I need to label faceted plots…

  2. tylerrinker says:

    Thanks for the feedback Luca 🙂

  3. Andrew says:

    I found i needed to enclose the mtcars$am with as.factor to get it to work:

    len <- length(levels(mtcars$gear)) * length(levels(as.factor(mtcars$am)))
    vars <- data.frame(expand.grid(levels(mtcars$gear), levels(as.factor(mtcars$am)))

    I am on R2.15.1 so not sure what everyone's thoughts on this were.

  4. tylerrinker says:

    @Andrew Thanks for the feedback. You are correct, I made a last minute switch that I didn’t transfer in the code. My apologies. The very first line I changed to am and not vs but didn’t change that in the blog post. It’s updated and should run without wrapping in as.factor.

    • Andrew says:

      thanks for the confirm. Great example – I have been looking for something like this for a while now, but was a bit lazy on experimenting with the geom_text function.

  5. Pingback: Add Text Annotations to ggplot2 Faceted Plot (an easier approach) | TRinker's R Blog

  6. Justin says:

    Nice workaround! Individually annotating facets is such a pain.

  7. Excellent tutorial. I like the detailed walk-through. I’m new to ggplot2, so this was great. Just wish I’d found it yesterday 🙂

  8. singlecoated says:

    This is, basically, superb. You saved me several hours and no less headaches. Thanks for sharing !

  9. I’ve been looking for something like this for days! Thank you so much 🙂

  10. Justine M says:

    Thank you! Very clear instructions – worked a treat!

  11. Kai says:

    Thank you very much for the nice demonstration! It works!

    By the way, by creating blank label names to a set of facets, one can basically produce texts on a designated number of facets.

  12. Vanessa says:

    Thanks for posting this! This really helped me visualize what the individual pieces of code mapped to (e.g. setting up the dataframe with data to be mapped using annotation). Greatly appreciative!

  13. Thank for post!, it is very clear and works perfectly!

  14. Just me says:

    Thanks, this helped a lot.

  15. Matt Wilkins says:

    Thanks for this excellent post! I’m running into a problem, though, as I also want to change fontface across facets. That is, my label is the Spearman’s rho correlation for each relationship and I want to embolden values based on significance. Apparently because plotmath expressions such as in your example (i.e. when parse=T) are treated differently, the fontface parameter is ignored 😦

    So, the problem is my code
    >annotate(“text”,x=X,y=Y, label=paste(“rho== “,round(xycor$rho,3)),parse=T, fontface=xycor$significancecode)

    bolds the significant values when parse=F, but not when the rho is rendered as a Greek letter when parse=T!

    Is there an easy way to do what I’m asking? It seems like the plotmath expression wants something like: bold(labelexpression), but this would seem to be a pretty messy solution, since each facet would need a separate expression…also, I’m not sure about the syntax.

    Thanks in advance!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s