赞
踩
追求系列化、高质量的R语言教程
ggplot2
包直接绘制的图形毕竟是有限的,不过它既然能被称作是绘图系统,自然具有可拓展性,只要明白它的工作原理,用户就可以自己定义该系统的函数了。
在介绍自定义方法之前,学堂君会先介绍另外两个函数:ggproto()
和layer()
。
本篇的目录如下:
1 ggproto函数
1.1 proto对象
1.2 ggproto对象
2 layer函数
3 定义新的统计变换函数
3.1 案例背景
3.2 定义ggproto对象
3.3 定义新函数
ggproto()
函数的功能是创建一个ggproto对象。ggproto对象是一种数据结构形式,又分为六个大类:Geom、Stat、Scale、Facet、Coord、Position,分别对应绘图系统的六大类函数。
每个大类对象又可以派生出小类,每小类对应着一个具体的函数,比如geom_point()
函数就对应着GeomPoint对象,stat_bin()
函数就对应着StatBin对象。自定义函数就是从创建小类对象开始的,而因为小类对象可以自定义,所以它的种类也是无限的。
ggproto对象源于proto
工具包的proto对象,本小节先简单介绍下proto对象的特点。proto对象的组成元素有两类:属性(field)和方法(method)。例如:
- library(proto)
- oo <- proto(x = 1,
- f = function(.) {.$x = .$x + 1
- return(.$x)}
- )
从形式上看,proto对象的定义格式和列表(list)相似;
上面定义的
oo
共包含两个元素:x
是一个普通的命名变量,即属性(field);f
则是一个函数,即方法(method)。
proto对象中的函数元素可以使用点号.
表示对象本身,并可以直接更新对象本身的属性值:
- ## 每次运行都是自身的一次迭代
- oo$f()
- ## [1] 2
-
- oo$f()
- ## [1] 3
-
- oo$x
- ## [1] 3
这种编程方式称为面向对象的编程(Object Oriented Programming, OOP)。对于R中的普通数据结构来说,是不能够实现自身的更新的,而是通过类似data <- f(data)
这种重命名的形式实现更新。
ggproto()
函数相比proto()
函数多了两个固定的参数`_class`
和`_inherit`
(均可缺省):
ggproto(`_class` = NULL, `_inherit` = NULL, ...)
`_class`:定义的ggproto对象小类的名称;类似GeomPoint、StatBin;
`_inherit`:继承的ggproto对象;函数允许用户在已有的ggproto对象的基础上进行改进以创建新的ggproto对象;继承的对象可以是大类也可以是小类。
ggproto对象使用self
代替了proto中的点号.
来表示对象本身:
- library(ggplot2)
- gg <- ggproto(x = 1,
- f = function(self) {self$x = self$x + 1
- return(self$x)}
- )
-
- gg$f()
- ## [1] 2
- gg$f()
- ## [1] 3
由于历史版本原因,ggplot2系统把几何图形函数(geom_*
)和统计变换函数(stat_*
)区分成为两类函数,但是实际上二者在很多时候都可以相互替换使用,这一点在基础推文中已有介绍。由于两类函数都可以用来创建图层,作者(Hadley Wickham等人)又将它们统一为layer()
函数:
- layer(
- geom = NULL,
- stat = NULL,
- data = NULL,
- mapping = NULL,
- position = NULL,
- params = list(), # 参数的参数
- inherit.aes = TRUE,
- check.aes = TRUE,
- check.param = TRUE,
- show.legend = NA,
- key_glyph = NULL,
- layer_class = Layer
- )
layer()
函数的参数一共有12个,而其他绘图参数如美学(aesthetics)参数col
、fill
等都以列表元素的形式放在params
参数中。
如果已经学会了使用ggplot2
的各类函数,我们可以很容易地把它替换成layer()
函数:
- library(ggplot2)
- ggplot(mpg, aes(displ, hwy)) + geom_point()
-
- ## 替换成layer函数
- ggplot(mpg, aes(displ, hwy)) +
- layer(
- geom = "point", stat = "identity", position = "identity",
- params = list(na.rm = FALSE)
- )
可以看出,layer()
函数像是各类函数的比较原始的形式,非常适合用来定义新函数。
在ggplot2绘图系统的几类函数中,比较容易进行自定义的是统计变换类函数。
在生存分析(2)中,学堂君曾使用如下代码(已简化)绘制了如下图形:
- library(survival)
- library(ggplot2)
- fit <- survfit(formula = Surv(futime, fustat) ~ 1,
- data = ovarian)
- data <- data.frame(t = fit$time, s = fit$surv)
-
- ggplot(data) +
- geom_step(aes(x = t, y = s), col = "red") +
- annotate("rect", xmin = head(data$t, -1),
- xmax = data$t[-1],
- ymin = 0, ymax = data$s[1:25],
- fill = "blue", alpha = 0.2) +
- theme_bw(base_size = 15) +
- theme(panel.grid = element_blank())
其目的是绘制点-点之间的阶梯线与x轴所围成的图形。已有的geom_area()
函数使用的是斜线与x轴围成的图形:
- ggplot(data, aes(x = t, y = s)) +
- geom_line(col = "red") +
- geom_area(fill = "blue", alpha = 0.2) +
- theme_bw(base_size = 15) +
- theme(panel.grid = element_blank())
解决方法是以矩形块(rect
)作为注释(annotate()
函数)添加在图形上:
- annotate("rect", xmin = head(data$t, -1),
- xmax = data$t[-1],
- ymin = 0, ymax = data$s[1:25],
- fill = "blue", alpha = 0.2)
据此,我们可以将这个过程封装成一个新函数。
在定义新函数之前,需要定义一个Stat对象的小类,这里命名为StepArea
。
我们需要在Stat大类的基础上进行定义。Stat大类所包含的元素名即属性和方法都是固定的,我们要做的就是根据需要定义或重定义其中的部分元素。
直接运行语句Stat
(不加小括号)就可以查看Stat大类所包含的元素:
Stat ## <ggproto object: Class Stat, gg> ## aesthetics: function ## compute_group: function ## compute_layer: function ## compute_panel: function ## default_aes: uneval ## extra_params: na.rm ## finish_layer: function ## non_missing_aes: ## optional_aes: ## parameters: function ## required_aes: ## retransform: TRUE ## setup_data: function ## setup_params: function
冒号后为
function
的是方法(method),其余为属性(field);大部分元素值是空的(即无默认值);
retransform
属性有默认值TRUE。
在这里,我们仅需要定义其中的required_aes
属性和compute_group
方法就可以了。前者为绘图所必需的美学参数,后者是把输入数据转换成绘图数据的函数。
- StepArea <- ggproto("StepArea", Stat,
- required_aes = c("x", "y"),
-
- compute_group = function(data, scales) {
- n = dim(data)[1]
- data.frame(xmin = data$x[1:(n-1)],
- xmax = data$x[2:n],
- ymin = 0,
- ymax = data$y[1:(n-1)])
- }
- )
绘图必需参数为
x
和y
;定义的
compute_group
函数(方法)是从输入数据中提取矩形块的左(xmin
)、右(xmax
)、下(ymin
)、上(ymax
)边界的坐标。
定义新函数还是使用function()
函数。因为定义的是统计变换类函数,所需参数直接从已有的stat_*
系列函数那里赋值就可以了,如果有新参数也加上(本篇不涉及)。这里因为新函数会调用geom_rect()
函数的功能,因此把geom
参数的默认值设置为rect
。
新函数内部直接调用layer()
函数进行绘图,其中stat
参数设置为上面定义的StepArea
,其他参数直接使用新函数的参数。新函数的...
参数表示继承自几何图形函数(对应geom
参数),需要放在layer()
函数的params
参数中。
新函数定义代码如下:
- stat_steparea <- function(mapping = NULL, data = NULL, geom = "rect",
- position = "identity", na.rm = FALSE, show.legend = NA,
- inherit.aes = TRUE, ...) {
-
- layer(
- stat = StepArea, data = data, mapping = mapping, geom = geom,
- position = position, show.legend = show.legend, inherit.aes = inherit.aes,
- params = list(na.rm = na.rm, ...)
- )
- }
使用示例:
- ggplot(data, aes(t,s)) +
- geom_step(aes(x = t, y = s), col = "red") +
- stat_steparea(fill = "blue", alpha = 0.2) + # 新函数
- theme_bw(base_size = 15) +
- theme(panel.grid = element_blank())
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。