Inverse of ggplotGrob?

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 ...

P2 <- inverse_of_ggplotGrob(G)

such that, we can continue to use basic ggplot syntax, ie
`P2 + ylab ("The Width of the Petal")`


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) {

  data.table(grob_index=strips, type=strip.type, group=strip.nms)

refill <- function(strip, colour){
  strip[["children"]][[1]][["gp"]][["fill"]] <- colour

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[!, grob_index]
  cols <- DT.strips[!, color]

  ## Fill in the appropriate colors, using refill()
  G$grobs[inds] <- mapply(refill, strip = G$grobs[inds], colour = cols, SIMPLIFY = FALSE)



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

It involves a little bit of a cheat annotation_custom(as.grob(plot),...), so it may not work for all circumstances:

Have a look at the ggpubr package: it has a function as_ggplot(). If your grob is not too complex it might be a solution!

I would also advise to have a look at the patchwork package which combine nicely ggplots... it is likely to not be what you are looking for but... have a look.

Need Your Help

How can I use cshtml files with Durandal? single-page-application durandal

I got the DurandalJS StarterKit template on VS2012... All works great...

Optimal data architecture for tagging, clouds, and searching (like StackOverflow)?

database-design tags full-text-search tag-cloud

I'd love to know how Stack Overflow's tagging and search is architected, because it seems to work pretty well.