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 }
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With