当前位置:   article > 正文

如何自定义ggplot2绘图系统的函数(1)——定义一个简单的统计变换函数

ggproto

追求系列化、高质量的R语言教程

ggplot2包直接绘制的图形毕竟是有限的,不过它既然能被称作是绘图系统,自然具有可拓展性,只要明白它的工作原理,用户就可以自己定义该系统的函数了。

在介绍自定义方法之前,学堂君会先介绍另外两个函数:ggproto()layer()

本篇的目录如下:

  • 1 ggproto函数

    • 1.1 proto对象

    • 1.2 ggproto对象

  • 2 layer函数

  • 3 定义新的统计变换函数

    • 3.1 案例背景

    • 3.2 定义ggproto对象

    • 3.3 定义新函数

1 ggproto函数

ggproto()函数的功能是创建一个ggproto对象。ggproto对象是一种数据结构形式,又分为六个大类:Geom、Stat、Scale、Facet、Coord、Position,分别对应绘图系统的六大类函数。

每个大类对象又可以派生出小类,每小类对应着一个具体的函数,比如geom_point()函数就对应着GeomPoint对象,stat_bin()函数就对应着StatBin对象。自定义函数就是从创建小类对象开始的,而因为小类对象可以自定义,所以它的种类也是无限的。

1.1 proto对象

ggproto对象源于proto工具包的proto对象,本小节先简单介绍下proto对象的特点。proto对象的组成元素有两类:属性(field)和方法(method)。例如:

  1. library(proto)
  2. oo <- proto(x = 1
  3.             f = function(.) {.$x = .$x + 1 
  4.             return(.$x)}
  5.             )
  • 从形式上看,proto对象的定义格式和列表(list)相似;

  • 上面定义的oo共包含两个元素:x是一个普通的命名变量,即属性(field);f则是一个函数,即方法(method)。

proto对象中的函数元素可以使用点号.表示对象本身,并可以直接更新对象本身的属性值:

  1. ## 每次运行都是自身的一次迭代
  2. oo$f()
  3. ## [12
  4. oo$f()
  5. ## [13
  6. oo$x
  7. ## [13

这种编程方式称为面向对象的编程(Object Oriented Programming, OOP)。对于R中的普通数据结构来说,是不能够实现自身的更新的,而是通过类似data <- f(data)这种重命名的形式实现更新。

1.2 ggproto对象

ggproto()函数相比proto()函数多了两个固定的参数`_class``_inherit`(均可缺省):

ggproto(`_class` = NULL, `_inherit` = NULL, ...)
  • `_class`:定义的ggproto对象小类的名称;类似GeomPoint、StatBin;

  • `_inherit`:继承的ggproto对象;函数允许用户在已有的ggproto对象的基础上进行改进以创建新的ggproto对象;继承的对象可以是大类也可以是小类。

ggproto对象使用self代替了proto中的点号.来表示对象本身:

  1. library(ggplot2)
  2. gg <- ggproto(x = 1
  3.               f = function(self) {self$x = self$x + 1 
  4.               return(self$x)}
  5.               )
  6. gg$f()
  7. ## [12
  8. gg$f()
  9. ## [13

2 layer函数

由于历史版本原因,ggplot2系统把几何图形函数(geom_*)和统计变换函数(stat_*)区分成为两类函数,但是实际上二者在很多时候都可以相互替换使用,这一点在基础推文中已有介绍。由于两类函数都可以用来创建图层,作者(Hadley Wickham等人)又将它们统一为layer()函数:

  1. layer(
  2.   geom = NULL,
  3.   stat = NULL,
  4.   data = NULL,
  5.   mapping = NULL,
  6.   position = NULL,
  7.   params = list(), # 参数的参数
  8.   inherit.aes = TRUE,
  9.   check.aes = TRUE,
  10.   check.param = TRUE,
  11.   show.legend = NA,
  12.   key_glyph = NULL,
  13.   layer_class = Layer
  14. )

layer()函数的参数一共有12个,而其他绘图参数如美学(aesthetics)参数colfill等都以列表元素的形式放在params参数中。

如果已经学会了使用ggplot2的各类函数,我们可以很容易地把它替换成layer()函数:

  1. library(ggplot2)
  2. ggplot(mpg, aes(displ, hwy)) + geom_point()
  3. ## 替换成layer函数
  4. ggplot(mpg, aes(displ, hwy)) +
  5.   layer(
  6.     geom = "point", stat = "identity", position = "identity",
  7.     params = list(na.rm = FALSE)
  8.   )
8685e33fa7acc759a7dd6d6b7d364610.png

可以看出,layer()函数像是各类函数的比较原始的形式,非常适合用来定义新函数。

3 定义新的统计变换函数

在ggplot2绘图系统的几类函数中,比较容易进行自定义的是统计变换类函数。

3.1 案例背景

生存分析(2)中,学堂君曾使用如下代码(已简化)绘制了如下图形:

  1. library(survival)
  2. library(ggplot2)
  3. fit <- survfit(formula = Surv(futime, fustat) ~ 1,
  4.                data = ovarian)
  5. data <- data.frame(t = fit$time, s = fit$surv) 
  6. ggplot(data) +
  7.   geom_step(aes(x = t, y = s), col = "red") +
  8.   annotate("rect", xmin = head(data$t, -1), 
  9.            xmax = data$t[-1],
  10.            ymin = 0, ymax = data$s[1:25],
  11.            fill = "blue", alpha = 0.2) +
  12.   theme_bw(base_size = 15) +
  13.   theme(panel.grid = element_blank())
1f62bd55e68643cabe7812d5228d1c16.png

其目的是绘制点-点之间的阶梯线与x轴所围成的图形。已有的geom_area()函数使用的是斜线与x轴围成的图形:

  1. ggplot(data, aes(x = t, y = s)) +
  2.   geom_line(col = "red") +
  3.   geom_area(fill = "blue", alpha = 0.2) +
  4.   theme_bw(base_size = 15) +
  5.   theme(panel.grid = element_blank())
71ee536580dce16103db8ea325c3d6f0.png

解决方法是以矩形块(rect)作为注释(annotate()函数)添加在图形上:

  1. annotate("rect", xmin = head(data$t, -1), 
  2.            xmax = data$t[-1],
  3.            ymin = 0, ymax = data$s[1:25],
  4.            fill = "blue", alpha = 0.2)

据此,我们可以将这个过程封装成一个新函数。

3.2 定义ggproto对象

在定义新函数之前,需要定义一个Stat对象的小类,这里命名为StepArea

我们需要在Stat大类的基础上进行定义。Stat大类所包含的元素名即属性和方法都是固定的,我们要做的就是根据需要定义或重定义其中的部分元素。

直接运行语句Stat(不加小括号)就可以查看Stat大类所包含的元素:

  1. Stat
  2. ## <ggproto object: Class Stat, gg>
  3. ##     aesthetics: function
  4. ##     compute_group: function
  5. ##     compute_layer: function
  6. ##     compute_panel: function
  7. ##     default_aes: uneval
  8. ##     extra_params: na.rm
  9. ##     finish_layer: function
  10. ##     non_missing_aes: 
  11. ##     optional_aes: 
  12. ##     parameters: function
  13. ##     required_aes: 
  14. ##     retransform: TRUE
  15. ##     setup_data: function
  16. ##     setup_params: function
  • 冒号后为function的是方法(method),其余为属性(field);

  • 大部分元素值是空的(即无默认值);retransform属性有默认值TRUE。

在这里,我们仅需要定义其中的required_aes属性和compute_group方法就可以了。前者为绘图所必需的美学参数,后者是把输入数据转换成绘图数据的函数。

  1. StepArea <- ggproto("StepArea", Stat,
  2.     required_aes = c("x""y"),
  3.     
  4.     compute_group = function(data, scales) {
  5.       n = dim(data)[1]
  6.       data.frame(xmin = data$x[1:(n-1)],
  7.                  xmax = data$x[2:n],
  8.                  ymin = 0,
  9.                  ymax = data$y[1:(n-1)])
  10.     }
  11. )
  • 绘图必需参数为xy

  • 定义的compute_group函数(方法)是从输入数据中提取矩形块的左(xmin)、右(xmax)、下(ymin)、上(ymax)边界的坐标。

3.3 定义新函数

定义新函数还是使用function()函数。因为定义的是统计变换类函数,所需参数直接从已有的stat_*系列函数那里赋值就可以了,如果有新参数也加上(本篇不涉及)。这里因为新函数会调用geom_rect()函数的功能,因此把geom参数的默认值设置为rect

新函数内部直接调用layer()函数进行绘图,其中stat参数设置为上面定义的StepArea,其他参数直接使用新函数的参数。新函数的...参数表示继承自几何图形函数(对应geom参数),需要放在layer()函数的params参数中。

新函数定义代码如下:

  1. stat_steparea <- function(mapping = NULL, data = NULL, geom = "rect",
  2.                           position = "identity", na.rm = FALSE, show.legend = NA, 
  3.                           inherit.aes = TRUE, ...) {
  4.   
  5.   layer(
  6.     stat = StepArea, data = data, mapping = mapping, geom = geom, 
  7.     position = position, show.legend = show.legend, inherit.aes = inherit.aes,
  8.     params = list(na.rm = na.rm, ...)
  9.   )
  10. }

使用示例:

  1. ggplot(data, aes(t,s)) +
  2.   geom_step(aes(x = t, y = s), col = "red") +
  3.   stat_steparea(fill = "blue", alpha = 0.2) + # 新函数
  4.   theme_bw(base_size = 15) +
  5.   theme(panel.grid = element_blank())
6ebdd996784a65a2030f7ff432da425f.png
e793ad0179887b264679940c34e334c3.jpeg
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号