如何确定ggplot2对象中每个图层的几何类型?

11
作为我已经创建的绘图中删除特定几何图形的努力的一部分(SO链接here),我想动态确定ggplot2对象的每个图层的几何图形类型。
假设我不知道添加图层的顺序,有没有办法动态查找具有特定几何图形的图层?如果像下面所示打印出图层,我可以看到图层存储在列表中,但似乎无法访问几何图形类型。
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())。


感谢以下回答,我能够编写一个动态删除层的函数:
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
}

能否提供一个小的可重现数据集,以方便我们测试您的代码? - Dason
糟糕,复制粘贴失败了。谢谢,@Dason - Erik Shilts
3个回答

11

ggplot 2.2更新: 如果你想要一个命名几何类型的字符字符串,你可以使用:

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

这会为每个图层的几何对象生成第一个类名称。在OP的示例中:

[1] "GeomRibbon" "GeomLine" 

以上答案中的代码已经不再适用于2.2版本, 接受的答案会返回两个NULL值,而其他答案将返回完整的ggproto对象。


我的回答哪个版本出了问题? - Dason
我不确定,但是2015年12月的第二个版本引入了许多重大变化,也许是这个原因?https://blog.rstudio.org/2015/12/21/ggplot2-2-0-0/ - arvi1000
1
那可能就是这样了。无论如何,我在帖子的顶部添加了一个编辑,将其重定向到你的答案,因为它似乎是最新的。 - Dason
1
鉴于我现在所做的更改,我将此作为被接受的答案。 - Erik Shilts

4

编辑:此答案已不再适用。当问题被提出时,它可以工作,并且可能在很长一段时间内可以工作,但对于适用于ggplot2>= 2.2.0的答案,请参见https://dev59.com/iGYr5IYBdhLWcg3wuMX9#43982598


如果我们只是想要获取每个项目的geom名称,则似乎在每个图层的$geom$objname部分中。

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

作为补充说明 - 如果你没有显式加载proto包,你无法使用 p$layers[[1]]$str() 的语法。 ggplot2在内部使用它,但是它会导入而不是使用Depends。请注意区别:
> 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 

1
感谢您解释为什么 $str() 不可用。 - IRTFM
五年后,这已经不适用于当前的ggplot版本了;发布了一个新的答案。 - arvi1000

2

以下是一种理解原型对象的方式。据我所知,它们就像环境一样,因此使用ls可以列出名称(甚至对于从父环境即绘图对象p中提取的项目也是如此):

 ls(p$layers[[1]])
# [1] "data"        "geom"        "geom_params" "inherit.aes" "mapping"    
# [6] "position"    "show_guide"  "stat"        "stat_params" "subset" 

 p$layers[[1]][["geom"]]
#geom_ribbon: 

 sapply( p$layers, "[[", "geom")
#---------------
[[1]]
geom_ribbon: 

[[2]]
geom_line: 

@Dason指出你可能想要一个字符向量作为结果,所以再次使用sapply和"[["应该能满足这种可能的需求:

 sapply( sapply( p$layers, "[[", "geom"), "[[", 'objname')
#[1] "ribbon" "line"

在ggproto设计中的变化包括将名称存放在更深的一层,即class-attributes的内部:
 lapply( sapply( p$layers, "[[", "geom"), function(x) attributes(x) )
#----------------
[[1]]
[[1]]$class
[1] "GeomRibbon" "Geom"       "ggproto"   


[[2]]
[[2]]$class
[1] "GeomLine" "GeomPath" "Geom"     "ggproto" 

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

你的sapply调用仍然返回proto对象,这对于处理不是理想的。如果你获取返回值中的objname部分,那么你会得到一个漂亮的字符字符串。 - Dason
修复了这个缺陷。 - IRTFM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接