美文网首页R语言
function()编写函数

function()编写函数

作者: 小贝学生信 | 来源:发表于2020-06-14 14:14 被阅读0次

    之前在R语言入门--第四节(数据管理进阶)笔记的最后部分以两个例子简单学习了利用function()自定义编写函数的方法,这次专门再学习下。

    自定义函数的目的主要是为了简化代码。有人曾说只要一段代码需要复制粘贴的次数超过两次(也就是说,同一段代码至少有 3 个副本),那么就应该考虑编写一个函数。

    一、function编写步骤

    1、为函数选择一个合适的名称

    • 最好是动词,描述函数的功能;
    • 多个单词时,用连接符,如_.等,选择自己常用的,保持自己的风格;
    • 有一族功能相似的函数,那么一定要确保它们具有一致的名称和参数,例如相同的前缀。

    2、列举出 function 中所用的输入

    • 编写时逻辑要清晰,理解这个函数的输入与输出;输入即是函数的参数;
    • 格式上为function后()里的部分;
    • 一般函数的参数可分为两类---数据与细节调节。具体后面会介绍。

    3、将已经编写好的代码放在函数体中

    • 格式上为紧跟function()后{}里的代码块;
    • 左大括号不应该自己占一行,而且后面要换行。右大括号应该自己占一行,除非后面跟着else。
    • 大括号中的代码一定要缩进,一般都会自动缩进的。

    例子

    函数功能:将数据中心化为0~1区间

    rescale01 <- function(x) {
    rng <- range(x, na.rm = TRUE)
    # 中间计算结果保存为命名变量是一种非常好的做法,因为这样可以让代码的意义更加清楚。
    (x - rng[1]) / (rng[2] - rng[1])
    }
    rescale01(c(0, 5, 10))
    
    df <- data.frame(
      a = rnorm(10),
      b = rnorm(10),
      c = rnorm(10),
      d = rnorm(10)
    )
    rescale01(df$a)
    

    当函数复杂时,可利用#加入注释,便于他人或者将来自己的理解。此外也可创建分节标记,快捷键Ctrl+Shift+R,方便代码管理。

    二、常见搭配--if条件语句

    • 当函数执行多种复杂功能时,尤其是有多个参数或者参数有多个选项时,则必须用到if条件语句。(可以参看开头那个链接笔记)
    • 基本框架--
    if (condition) {
      # 条件为真时执行的代码
    } else {
      # 条件为假时执行的代码
    }
    

    注意点

    • condition逻辑表达式的值要么是 TRUE ,要么是 FALSE

    在测试相等关系时,一定要小心, == 是向量化的,很容易输出多个值,即要先检查长度是否相同。或者推荐使用非向量化的identical() 函数。 identical() 非常严格,总是返回一个 TRUE 或者一个 FALSE ,并且不限制参数类型。

    • 当需要多个表达式一起决定时,可根据情况选择II(或)、&&(与)操作符。

    教材说不能用|&,但我看链接笔记里也确实用到了&结构,存疑!

    • 当参数有多个选项时,可使用switch()函数,可参见链接笔记第二个例子。或者见下
    compute <- function(x, y, op) {
    switch(op,
     plus = x + y,
     minus = x - y,
     times = x * y,
     divide = x / y,
     stop("Unknown op!")
    ) 
    }
    compute(1, 2, "pus")
    

    三、函数参数

    1、参数分类

    如前所述函数的参数通常分为两大类:一类提供需要进行计算的数据,另一类控制计算过程的细节。举例----

    • 在 mean() 函数中,数据是 x ,细节则是从x 前后两端( trim )移除多大比例的数据,以及如何处理缺失值( na.rm )。
    • 在 t.test() 函数中,数据是 x 和y ,检验的细节则是 alternative 、 mu 、 paired 、 var.equal 以及 conf.level 等设置。

    2、特殊的数据参数

    • ... 三个点表示可提供任意数量的字符串作为数据,即捕获到任意数量的未匹配参数。注意是未匹配的参数!
    ?sum
    sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    ?stringr::str_c
    stringr::str_c("a", "b", "c", "d", "e", "f")
    

    3、设置默认细节调节参数

    • 数据参数应该放在最前面,细节参数则放在后面,而且一般都有默认值。
    • 设置默认值的方式与使用命名参数调用函数的方式是一样的,在括号内设置好即可。
    # 使用近似正态分布计算均值两端的置信区间
    mean_ci <- function(x, conf = 0.95) {
      se <- sd(x) / sqrt(length(x))
      alpha <- 1 - conf
      mean(x) + se * qnorm(c(alpha / 2, 1 - alpha / 2))
    }
    
    x <- runif(100)
    mean_ci(x)
    mean_ci(x, conf = 0.99)
    

    调用函数时,最好遵循参数的书写格式。即在其中 = 的两端都加一个空格。逗号后面应该总是加一个空格,逗号前面则不要加空格。此外值得注意的一点是我们经常省略数据参数的名称。

    4、检查参数值

    • 使函数使用更加友好,应该事先检查参数的有效性。
    • 若不符合既定要求,则返回提示性报错。
    (1)stop函数

    检查一个要求时方便

    wt_mean <- function(x, w) {
      if (length(x) != length(w)) {
        stop("`x` and `w` must be the same length", call. = FALSE)
      }
      sum(w * x) / sum(x)
    }
    wt_mean(1:5,6:10)
    
    (2)stopifnot()函数

    可检查多个参数

    wt_mean <- function(x, w, na.rm = FALSE) {
      stopifnot(is.logical(na.rm), length(na.rm) == 1)
      stopifnot(length(x) == length(w))
      if (na.rm) {
        miss <- is.na(x) | is.na(w)
        x <- x[!miss]
        w <- w[!miss]
      }
      sum(w * x) / sum(x)
    }
    wt_mean(1:6, 6:1, na.rm = "foo")
    

    5、常见通用参数

    • 一般参数名也是可自己任意设置,但也要有意义。
    • R 中有一些非常短的通用名称,值得我们编写函数时借鉴
      x, y, z :向量。
      w :权重向量。
      df:数据框。
      i, j :数值索引(通常用于表示行和列)。
      n :长度或行的数量。
      p :列的数量。
      na.rm:是否需要除去缺失值。

    将 na.rm 的默认值设为 FALSE 是情有可原的,因为缺失值有时是非常重要的。虽然代码中经常使用的是 na.rm = TRUE ,但是通过默认设置不声不响地忽略缺失值并不是一种良好的做法。

    四、函数返回值

    • 函数的返回值通常是最后一个语句的值;
    • 也可以通过 return() 语句提前返回一个值。
    • 如果函数功能能够返回多个值,那么可将结果都归为一个list列表里,最后将list导出即可。可参考开头链接笔记第一个例子。

    补充:管道符

    • magrittr包的%>%函数
      关于管道符我的理解就是把左边的数据通过管道符传给右边的函数,作为其数据参数。而这个函数的处理结果,若为数据,还可继续通过管道符传给下一个函数,如此循环下去...
    • 意义在于可以在后期用于优化代码,减少不必要的中间变量。
    mtcars$cyl %>%
      table() %>%
      barplot()
    

    支持管道符的函数类型

    • 我的理解就是不修改,变动数据参数时,这些函数会“悄悄地”返回第一个参数,因此,默认情况下,第一个参数不显示在输出中,但仍然可以由管道操作使用。
    • 例如绘图、保存、统计类函数等
    show_missings <- function(df) {
      n <- sum(is.na(df))
      cat("Missing values: ", n, "\n", sep = "")
      invisible(df)
    }
    show_missings(mtcars)
    library(dplyr)
    mtcars %>%
      show_missings() %>%
      mutate(mpg = ifelse(mpg < 20, NA, mpg)) %>%
      show_missings()
    

    相关文章

      网友评论

        本文标题:function()编写函数

        本文链接:https://www.haomeiwen.com/subject/bdritktx.html