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:
thank you so much. this saved me.
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.
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’
@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.
Dear Tylerrinker,
Thank you very much for your kind reply. You indeed make our, at least my life much easier. Thanks a lot.
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?
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!