所有的ggplot2对象都建立自"ggproto"这套面向对象编程系统,因此想要创建出自己的一套图层,而不是简单的对已有图层进行累加,那么就需要学习"ggproto"。
创建新的stat
最简单的stat
我们会从一个最简单的stat开始: 根据已有的一组点,用一个凸壳(convex hull)包围他。
第一步,我们创建一个继承自Stat
的"ggproto"对象
StatChull <- ggproto("StatChull", Stat,
compute_group = function(data, scales){
data[chull(data$x, data$y), , drop=FALSE]
},
required_aes = c("x","y")
)
在"ggproto"函数中,前两个是固定项,分别是类名和继承的"ggproto"类。而后续内容则是和你继承的类相关,例如compute_group()
方法负责计算,required_aes
则列出哪些美学属性(aesthetics)必须要存在,这两个都继承自Stat
,可以用?Stat
查看更多信息。。
第二步,我们开始写一个图层。由于历史设计原因,Hadley将其称作stat_()
或geom_()
。但实际上,Hadley认为layer_()
可能更准确些,毕竟每一个图层都或多或少的有"stat"和"geom"。
所有的图层都遵循相同的格式,即你在function
中声明默认参数,然后调用layer()
函数,将...
传递给params
参数。在...
的参数既可以是"geom"的参数(如果你要做一个stat封装),或者是"stat"的参数(如果你要做geom的封装),或者是将要设置的美学属性. layer()
会小心的将不同的参数分开并确保它们存储在正确的位置:
stat_chull <- function(mapping = NULL, data = NULL, geom = "polygon",
position = "identity", na.rm = TRUE, show.legend = NA,
inherit.aes = TRUE, ...){
layer(
stat = StatChull, data = data, mapping = mapping, geom = geom,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, ...)
)
}
(注, 在写R包的时候要注意用ggplot2::layer()
或在命名空间中导入layer()
, 否则会因找到函数而报错)
当我们写好了图层函数后,我们就可以尝试这个新的"stat"了
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
stat_chull(fill = NA, colour= "black")
simple-stat
(后续我们会学习如何通过设置"geom"的默认值,来避免声明fill=NA
)
一旦我们构建了这种基本的对象,ggplot2将会给我们带来极大的自由。举个例子,ggplot2自动保留每组中不变的美学属性,也就是说你可以分组绘制一个凸壳:
ggplot(mpg, aes(displ, hwy, colour = drv)) +
geom_point() +
stat_chull(fill = NA)
add group chull
我们还可以覆盖默认的图层,来以不同的形式展现凸壳:
ggplot(mpg, aes(displ, hwy)) +
stat_chull(geom = "point", size = 4, colour = "red") +
geom_point()
different chull