function()编写函数
2020-06-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()