Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot2: How to inspect every element of a plot using ggplot_build()?

Tags:

r

ggplot2

Is there a way to search the entire output from ggplot_build() (or any other function), almost like searching the complete content of every subdirectory of a folder?


The details:

I was looking for a solution to Retrieve values for axis labels in ggplot2_3.0.0, and one of the early answers revealed that, depending on the ggplot2 version, the correct answer most likely would contain the parts $layout and / or $x.labels in the output from ggplot_build(g). So I started checking the ggplot_build() output each step of the way. One of the steps looks like the output below.

Snippet 1:

ggplot_build(g)$layout

Output 1:

<ggproto object: Class Layout, gg>
    coord: <ggproto object: Class CoordCartesian, Coord, gg>
        aspect: function
        clip: on

        [...]

    map_position: function
    panel_params: list
    panel_scales_x: list
    panel_scales_y: list
    render: function

        [...]

    ylabel: function
    super:  <ggproto object: Class Layout, gg>
>

And deep down there, under panel params, x.labels can be found along with lots of useful information like this:

Snippet 2:

ggplot_build(g)$layout$panel_params

Output 2:

[[1]]
[[1]]$`x.range`
[1]  7.7 36.3

[[1]]$x.labels
[1] "10" "15" "20" "25" "30" "35"

[[1]]$x.major
[1] 0.08041958 0.25524476 0.43006993 0.60489510 0.77972028 0.95454545

And it can be referenced directly like this:

Snippet 3:

ggplot_build(g)$layout$panel_params[[1]]$x.labels

Output 3:

[1] "10" "15" "20" "25" "30" "35"

My attempt for a more elegant approach:

I was certain I could do this with capture.output() like you can with str() as described here, but as far as I can tell, you won't find x.labels there either. I'm not going to flood the question with that output since it's about 300 lines long.

Thank you for any suggestions!


like image 535
vestland Avatar asked Aug 10 '18 17:08

vestland


People also ask

What does %>% do in ggplot?

%>% is a pipe operator reexported from the magrittr package. Start by reading the vignette. Adding things to a ggplot changes the object that gets created. The print method of ggplot draws an appropriate plot depending upon the contents of the variable.

Which ggplot2 connects multiple operations?

Integrating the pipe operator with ggplot2 The pipe operator can also be used to link data manipulation with consequent data visualization.

Which argument of ggplot can be used to add customization to plots?

To customize the plot, the following arguments can be used: alpha, color, dotsize and fill. Learn more here: ggplot2 dot plot.

Which operator allows you to add objects to a ggplot?

Elements that are normally added to a ggplot with operator + , such as scales, themes, aesthetics can be replaced with the %+% operator.


1 Answers

This function goes through a nested list structure and finds the paths through that structure that contain a given character string:

find_name <- function(obj, name) {

  # get all named paths through obj
  find_paths <- function(obj, path) {

    if ((!is.list(obj) && is.null(names(obj))) || identical(obj, .GlobalEnv)) {
      return (path)
    } else {
      if (is.null(names(obj))) {
        return(c(path,
                 lapply(seq_along(obj), function(x) find_paths(obj[[x]], paste0(path, "[[", x, "]]")))
              ))
      } else {
        return(c(path,
                 lapply(names(obj), function(x) find_paths(obj[[x]], paste(path, x, sep = "$")))
              ))
      }  
    }

  }

  # get all the paths through the nested structure
  all_paths <- unlist(find_paths(obj, deparse(substitute(obj))))

  # find the requested name
  path_to_name <- grep(paste0("\\$", name, "$"), all_paths, value = TRUE)

  return (path_to_name)
}

Here is an example of using this function with a ggplot_built object:

library(ggplot2)
p <- ggplot(mtcars) + geom_point(aes(x = disp, y = mpg, col = as.factor(cyl)))
gb <- ggplot_build(p)
find_name(gb, "x.labels")
## [1] "gb$layout$panel_params[[1]]$x.labels"

You can also directly get the contents of x.labels:

eval(parse(text = find_name(gb, "x.labels")))
## [1] "100" "200" "300" "400"

A few remarks on how this works:

  • The function find_paths() goes through the nested structure and returns all "paths" through the structure in a form similar to "gb$layout$panel_params[[1]]$x.labels".
  • The nested structure can contain named lists, unnamed lists, named "lists" that have another class (and thus return FALSE for is.list() and environments. One has to take care of all these situations.
  • A particular caveat is that a ggplot_built contains a reference to the global environment (gb$layout$facet_params$plot_env), which leads to an infinite loop if it is not properly treated.
  • The result of find_paths() is a nested list again, but the structure can easily be simplified with unlist().
  • The last step is to extract those paths that contain the name one is looking for. The regular expression I use ensures that only elements that exactly match the given name are returned. As an example, find_name(gb, "x") will not return "gb$layout$panel_params[[1]]$x.labels".

I have tested the function with the ggplot_built object from my example and with a nested list. I cannot guarantee that it works for all situations.

like image 87
Stibu Avatar answered Oct 21 '22 00:10

Stibu