Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inverse of ggplotGrob?

Tags:

r

ggplot2

grob

I have a function which manipulates a ggplot object, by converting it to a grob and then modifying the layers. I would like the function to return a ggplot object not a grob. Is there a simple way to convert a grob back to gg?

The documentation on ggplotGrob is awfully sparse.
Simple example:

P <- ggplot(iris) + geom_bar(aes(x=Species, y=Petal.Width), stat="identity")  G <- ggplotGrob(P) ... some manipulation to G ...  ## DESIRED:  P2 <- inverse_of_ggplotGrob(G)  such that, we can continue to use basic ggplot syntax, ie `P2 + ylab ("The Width of the Petal")` 

UPDATE:

To answer the question in the comment, the motivation here is to modify the colors of facet labels programmatically, based on the value of label name in each facet. The functions below work nicely (based on input from baptise in a previous question).

I would like for the return value from colorByGroup to be a ggplot object, not simply a grob.

Here is the code, for those interested

get_grob_strips <- function(G, strips=grep(pattern="strip.*", G$layout$name)) {    if (inherits(G, "gg"))     G <- ggplotGrob(G)   if (!inherits(G, "gtable"))     stop ("G must be a gtable object or a gg object")    strip.type <- G$layout[strips, "name"]   ## I know this works for a simple    strip.nms <- sapply(strips, function(i) {      attributes(G$grobs[[i]]$width$arg1)$data[[1]][["label"]]   })    data.table(grob_index=strips, type=strip.type, group=strip.nms) }   refill <- function(strip, colour){   strip[["children"]][[1]][["gp"]][["fill"]] <- colour   return(strip) }  colorByGroup <- function(P, colors, showWarnings=TRUE) { ## The names of colors should match to the groups in facet   G <- ggplotGrob(P)   DT.strips <- get_grob_strips(G)    groups <- names(colors)   if (is.null(groups) || !is.character(groups)) {     groups <- unique(DT.strips$group)     if (length(colors) < length(groups))       stop ("not enough colors specified")     colors <- colors[seq(groups)]     names(colors) <- groups   }     ## 'groups' should match the 'group' in DT.strips, which came from the facet_name   matched_groups <- intersect(groups, DT.strips$group)   if (!length(matched_groups))     stop ("no groups match")   if (showWarnings) {       if (length(wh <- setdiff(groups, DT.strips$group)))         warning ("values in 'groups' but not a facet label: \n", paste(wh, colapse=", "))       if (length(wh <- setdiff(DT.strips$group, groups)))         warning ("values in facet label but not in 'groups': \n", paste(wh, colapse=", "))   }    ## identify the indecies to the grob and the appropriate color   DT.strips[, color := colors[group]]   inds <- DT.strips[!is.na(color), grob_index]   cols <- DT.strips[!is.na(color), color]    ## Fill in the appropriate colors, using refill()   G$grobs[inds] <- mapply(refill, strip = G$grobs[inds], colour = cols, SIMPLIFY = FALSE)    G } 
like image 800
Ricardo Saporta Avatar asked Oct 22 '14 02:10

Ricardo Saporta


2 Answers

I would say no. ggplotGrob is a one-way street. grob objects are drawing primitives defined by grid. You can create arbitrary grobs from scratch. There's no general way to turn a random collection of grobs back into a function that would generate them (it's not invertible because it's not 1:1). Once you go grob, you never go back.

You could wrap a ggplot object in a custom class and overload the plot/print commands to do some custom grob manipulation, but that's probably even more hack-ish.

like image 108
MrFlick Avatar answered Sep 29 '22 07:09

MrFlick


You can try the following:

p = ggplotify::as.ggplot(g) 

For more info, see https://cran.r-project.org/web/packages/ggplotify/vignettes/ggplotify.html

It involves a little bit of a cheat annotation_custom(as.grob(plot),...), so it may not work for all circumstances: https://github.com/GuangchuangYu/ggplotify/blob/master/R/as-ggplot.R

like image 26
user20061 Avatar answered Sep 29 '22 06:09

user20061