Add Text Annotations to ggplot2 Faceted Plot (an easier approach)

I recently posted a blog about adding text to a ggplot2 faceted plot (LINK).

I was unhappy with the amount of time it takes to create the text data frame to then label the plot. And then yesterday when the new version of ggplot2 0.9.2 was announced I got to reading about how ggplot2 objects are stored and I decided that I could extract a great deal of the information for plotting the text directly from the ggplot2 object.

After I did it I decided to wrap the function up into a package that I can add more ggplot2 extension functions to in the future.

Optionally Download the Package:

 
install_github("acc.ggplot2", "trinker")
library(acc.ggplot2)

Here’s the Function Code and a Few Examples:

 
library(ggplot2)

qfacet_text <- function(ggplot2.object, x.coord = NULL, y.coord = NULL, 
    labels = NULL, ...) {
    require(ggplot2)
    dat <- ggplot2.object$data
    rows <- ggplot2.object$facet[[1]][[1]]
    cols <- ggplot2.object$facet[[2]][[1]]
    fcol <- dat[, as.character(cols)]
    frow <- dat[, as.character(rows)]
    len <- length(levels(factor(fcol))) *  length(levels(factor(frow)))
    vars <- data.frame(expand.grid(levels(factor(frow)), levels(factor(fcol))))
    colnames(vars) <- c(as.character(rows), as.character(cols))
    if (any(class(ggplot2.object) %in% c("ggplot", "gg"))) {
        if (is.null(labels)) {
            labels <- LETTERS[1:len]
        }
        if (length(x.coord) == 1) {
           x.coord <- rep(x.coord, len)
        }
        if (length(y.coord) == 1) {
           y.coord <- rep(y.coord, len)
        }
        text.df <- data.frame(x = x.coord, y = y.coord, vars, labs=labels)
    } else {
        if (class(ggplot2.object) == "qfacet") {
            text.df <- ggplot2.object$dat
            if (!is.null(x.coord)) {
                text.df$x.coord <- x.coord
            }
            if (!is.null(y.coord)) {
                text.df$y.coord <- y.coord
            }
            if (!is.null(labels)) {
                text.df$labs <- labels
            }
            ggplot2.object <- ggplot2.object$original
        }
    }
    p <- ggplot2.object + geom_text(aes(x, y, label=labs, group=NULL), 
        data=text.df, ...)
    print(p)
    v <- list(original = ggplot2.object, new = p, dat = text.df)
    class(v) <- "qfacet"
    invisible(v)
}

Examples (using the same basic examples as my previous blog post):

 
#alter mtcars to make some variables 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()  

z <- qfacet_text(ggplot2.object = p, x.coor = 33, y.coor = 2.2, labels = 1:6, color="red")
str(z); names(z)  #look at what's returned

#approach 1 (alter the text data frame and pass the qfacet object)
z$dat[5, 1:2] <- c(15, 5)
qfacet_text(z, color="red")

#approach 2 (alter the original ggplot object)
qfacet_text(p, x = c(33, 33, 33, 33, 15, 33), 
    y = c(2.2, 2.2, 2.2, 2.2, 5, 2.2), 1:6, color="red")

#all the same things you can pass to geom_text qfacet_text takes
qfacet_text(z, labels = paste("beta ==", 1:6), 
    size = 3, color = "grey50", parse = TRUE)

Notice at the end you can pass qfacet_text a ggplot object or an object from qfacet_text. The qfacet_text function invisibly returns a list with the original ggplot2 object, the new ggplot2 object and the text data frame. This enables the user to alter the coordinates of the data frame and return the the qfacet_text object back to qfacet_text, thus altering the text position. There’s actual documentation for this package and function so ?qfacet_text should get you a help file with the same example.

PS this gave me a chance to actually run roxygen2 for the first time to create documentation. Also a pretty slick Hadley Wickham package.

The Plot:
ggplot facet with text

Advertisement

About tylerrinker

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

8 Responses to Add Text Annotations to ggplot2 Faceted Plot (an easier approach)

  1. brandi says:

    thank you so much. this saved me.

  2. Dong says:

    In your example, when i use facet_grid(~gear), and run p$facet[[1]][[1]], i will get such error:
    ‘Error in p$facet[[1]][[1]] : subscript out of bounds’, but i could still add text.
    However, when i run my data, i get same error ”Error in p$facet[[1]][[1]] : subscript out of bounds”, then i fail in adding text.

    Do you know why? thanks.

  3. Dong says:

    again, if this time i use facet_grid(~am), and i am not able to add text any more.
    if i run qfacet_text(p, x= c(15,30), y=c(4,5), 1:2), i will get error ‘Error in ggplot2.object$facet[[1]][[1]] : subscript out of bounds’

  4. tylerrinker says:

    @Dong, thanks for your feedback. I’m guessing you grabbed the code here and are trying to run it. This post was from when I first developed the function. Generally, after I blog about something I don’t alter the code on the blog, instead I update the code @github. This makes workflow more reasonable. I actually house the qfacet_text at my plotflow repo but have renamed it ggfaxt. I also still house it, though will not maintain (update it) it, at acc.ggplot2. The install_github(“acc.ggplot2”, “trinker”) or install_github(“plotflow”, “trinker”) makes everyone’s life simpler. If you wish to grab the code and run it from the command line, that’s the beauty of GitHub, the code is open so go there and grab it:

    https://github.com/trinker/plotflow/blob/master/R/ggfaxt.R

    So try this and it should work as expected.

    • Dong says:

      Dear Tylerrinker,
      Thank you very much for your kind reply. You indeed make our, at least my life much easier. Thanks a lot.

  5. Lara R says:

    Hi! Thanks for the post, I’ve used it but now I am trying to save a png file of the figure but I am having trouble figuring this out. How would you do it?

  6. tylerrinker says:

    Use the built in png function

    • So following your example, I have a facet grid plot, “p”
      then I add a qfacet_text line called “z”
      Then,
      >png(“plot.png”)
      >plot(p)
      >dev.off()

      this saved png won’t include the text annotations
      I tried doing
      >p=p+z
      But gives an error. Any other idea?
      Thanks!

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