Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
285 views
in Technique[技术] by (71.8m points)

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

## 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: 
", paste(wh, colapse=", "))
      if (length(wh <- setdiff(DT.strips$group, groups)))
        warning ("values in facet label but not in 'groups': 
", 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
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...