Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine the geom type of each layer of a ggplot2 object?

As part of an effort to remove a specific geom from a plot I've already created (SO link here), I'd like to dynamically determine the geom type of each layer of a ggplot2 object.

Assuming I don't know the order in which I added layers, is there a way to dynamically find layers with a specific geom? If I print out the layers like I do below I can see that the layers are stored in a list, but I can't seem to access the geom type.

library(ggplot2)
dat <- data.frame(x=1:3, y=1:3, ymin=0:2, ymax=2:4)
p <- ggplot(dat, aes(x=x, y=y)) + geom_ribbon(aes(ymin=ymin, ymax=ymax), alpha=0.3) + geom_line()
p$layers

[[1]]
mapping: ymin = ymin, ymax = ymax 
geom_ribbon: na.rm = FALSE, alpha = 0.3 
stat_identity:  
position_identity: (width = NULL, height = NULL)

[[2]]
geom_line:  
stat_identity:  
position_identity: (width = NULL, height = NULL)

I'm not familiar with proto objects and things I've tried from the proto documentation don't seem to work (e.g. p$layers[[1]]$str()).


Thanks to the answers below I was able to come up with a function that removes a layer dynamically:

remove_geom <- function(ggplot2_object, geom_type) {
  layers <- lapply(ggplot2_object$layers, function(x) if(x$geom$objname == geom_type) NULL else x)
  layers <- layers[!sapply(layers, is.null)]

  ggplot2_object$layers <- layers
  ggplot2_object
}
like image 420
Erik Shilts Avatar asked Nov 19 '12 16:11

Erik Shilts


2 Answers

ggplot 2.2 update: If what you want is a character string naming the geom type, you can use:

sapply(p$layers, function(x) class(x$geom)[1])

which yields the first class name for the geom object of each layer. In the OP's example:

[1] "GeomRibbon" "GeomLine" 

The code in the answers above no longer give the results shown for version 2.2 The accepted answer yields two NULL values, and the other answer yields full ggproto objects.

like image 185
arvi1000 Avatar answered Sep 17 '22 23:09

arvi1000


Edit: This answer is no longer current. It worked when the question was asked and presumably for quite some time after but for an answer that works with ggplot2 >= 2.2.0 see https://stackoverflow.com/a/43982598/1003565


If we're just looking to get the geom name for each item this appears to be in the $geom$objname part of each layer.

p$layers[[1]]$geom$objname
#[1] "ribbon"
lapply(p$layers, function(x){x$geom$objname})
#[[1]]
#[1] "ribbon"
#
#[[2]]
#[1] "line"

As an added note - the reason you couldn't use the p$layers[[1]]$str() syntax is (probably) because you didn't explicitly load the proto package. ggplot2 uses it internally but it imports it instead of using Depends. Notice the difference:

> library(ggplot2)
> dat <- data.frame(x=1:3, y=1:3, ymin=0:2, ymax=2:4)
> p <- ggplot(dat, aes(x=x, y=y)) + geom_ribbon(aes(ymin=ymin, ymax=ymax), alpha=0.3) + geom_line()
> p$layers
[[1]]
mapping: ymin = ymin, ymax = ymax 
geom_ribbon: na.rm = FALSE, alpha = 0.3 
stat_identity:  
position_identity: (width = NULL, height = NULL)

[[2]]
geom_line:  
stat_identity:  
position_identity: (width = NULL, height = NULL)

> p$layers[[1]]$str()
Error: attempt to apply non-function
> library(proto)
> p$layers[[1]]$str()
proto object 
 $ geom_params:List of 2 
 $ mapping    :List of 2 
 $ stat_params: Named list() 
 $ stat       :proto object  
  ..parent: proto object  
 .. .. parent: proto object  
 $ inherit.aes: logi TRUE 
 $ geom       :proto object  
  ..parent: proto object  
 .. .. parent: proto object  
 $ position   :proto object  
  ..parent: proto object  
 .. .. parent: proto object  
 .. .. .. parent: proto object  
 $ subset     : NULL 
 $ data       : list() 
  ..- attr(*, "class")= chr "waiver" 
 $ show_guide : logi NA 
like image 20
Dason Avatar answered Sep 17 '22 23:09

Dason