Code
mean(1:10)[1] 5.5
Code
sd(1:10)[1] 3.02765
Code
sum(1:10)[1] 55
通过本章学习,你将能够:
你已经用过很多 R 自带的函数:
mean(1:10)[1] 5.5
sd(1:10)[1] 3.02765
sum(1:10)[1] 55
👉 mean、sd、sum 都是 R 已经帮你写好的函数。你只需要调用它们。
但是,如果 R 没有你需要的函数呢?比如你想算”身高的 BMI 指数”,R 没有 calc_bmi() 这个函数。
👉 这时你就需要自己写一个函数。
假设你有三个班级的成绩数据,需要分别计算平均分:
# 不写函数:重复劳动
class1 <- c(80, 85, 90, 78, 92)
mean(class1); sd(class1); median(class1)
class2 <- c(75, 88, 82, 91, 79)
mean(class2); sd(class2); median(class2)
class3 <- c(88, 84, 76, 95, 81)
mean(class3); sd(class3); median(class3)每次都要把 mean()、sd()、median() 写一遍,很容易漏掉或写错。
# 写函数:一次定义,反复使用
describe <- function(x) {
list(mean = mean(x), sd = sd(x), median = median(x))
}
describe(class1)
describe(class2)
describe(class3)👉 函数的本质:把一段可重复使用的代码封装起来,取一个名字。之后每次需要这段逻辑时,调用函数名即可,不用再重写代码。
R 用 function 关键字创建函数。一个函数由三部分组成:
函数名 <- function(参数列表) {
函数体
}
| 部分 | 写在 | 作用 |
|---|---|---|
| 参数列表 | 小括号 ( ) 里 |
函数的”输入”,告诉函数要处理什么数据 |
| 函数体 | 大括号 { } 里 |
函数的”计算过程”,具体怎么处理数据 |
| 返回值 | 函数体的最后一行,或 return() |
函数的”输出”,把计算结果还回去 |
👉 可以用 formals()、body()、environment() 来查看一个函数的三个组成部分:
f02 <- function(x, y) {
x + y
}
formals(f02) # 查看参数$x
$y
body(f02) # 查看函数体{
x + y
}
environment(f02) # 查看函数所在环境<environment: R_GlobalEnv>
# 写一个求平方的函数
square <- function(x) {
x^2
}
# 调用函数
square(5) # 25[1] 25
square(10) # 100[1] 100
👉 整个流程:
square ← 给函数取名字function(x) ← 告诉 R:这个函数接受一个参数 x{ x^2 } ← 函数体:计算 x 的平方square(5) ← 调用函数时,5 被赋值给 x,然后计算 5^2,返回 25写一个函数 `cube()`,输入一个数,返回它的立方(三次方)。写一个函数 `double_it()`,输入一个数,返回它的两倍。# 立方
cube <- function(x) {
x^3
}
#cube(3) # 27
#cube(5) # 125
# 加倍
double_it <- function(x) {
2 * x
}
#double_it(7) # 14
#double_it(10) # 20最简单的函数,不需要任何输入,每次执行一样的事情:
say_hello <- function() {
"Hello, World!"
}
say_hello() # 注意:即使没有参数,括号也必须写[1] "Hello, World!"
👉 忘记写 () 是新手最常见的错误之一。say_hello(不带括号)返回的是函数本身,不是执行结果。
参数就是函数的”输入”。不同输入 → 不同输出:
greet <- function(name) {
paste0("你好,", name, "!")
}
greet("小明")[1] "你好,小明!"
给参数设定默认值:调用时如果省略该参数,就自动使用默认值。
add <- function(x, y = 10) {
x + y
}
add(5) # 15(y 自动用默认值 10)[1] 15
add(5, 20) # 25(y 被指定为 20,覆盖了默认值)[1] 25
👉 有默认值的参数是可选的;没有默认值的参数是必须提供的。
调用函数时有两种传参方式:
divide <- function(x, y) {
x / y
}
# 按位置:第 1 个参数给 x,第 2 个给 y
divide(10, 2) # 5[1] 5
👉 常见陷阱:按位置传参时,顺序搞反了结果完全不对:
divide(2, 10) # 0.2 —— 不是 5!因为 x=2, y=10[1] 0.2
👉 安全建议:不确定参数顺序时,直接写参数名。
写一个 `power()` 函数,计算 `x` 的 `n` 次方。`n` 的默认值是 2(默认求平方)写一个 `calc_bmi()` 函数,输入身高(米)和体重(千克),返回 BMI。公式:BMI = 体重 / 身高的平方。# 2-1 幂函数(n 默认 2)
power <- function(x, n = 2) {
x^n
}
#power(5) # 25(默认平方)
#power(5, 3) #
# 2-2 BMI 计算
calc_bmi <- function(weight, height) {
weight / (height^2)
}
#calc_bmi(70, 1.75) # 22.86R 函数有两种方式返回结果:
不写 return(),函数自动把最后一个表达式的值返回:
add <- function(x, y) {
x + y # 最后一个表达式,自动返回它的值
}
add(3, 7) # 10[1] 10
用 return() 明确指定返回什么。一旦执行到 return(),函数立即退出:
check <- function(score) {
if (score >= 60) {
return("及格")
} else
return("不及格")
}
check(75) # "及格"[1] "及格"
check(55) # "不及格"[1] "不及格"
👉 return() 的两个作用:
👉 什么时候必须用 return()?
R 函数一次只能返回一个东西。如果要返回多个结果,用 list() 把它们打包在一起:
describe <- function(x) {
list(
n = length(x),
mean = mean(x),
median = median(x),
sd = sd(x)
)
}
result <- describe(c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
result # 查看全部$n
[1] 10
$mean
[1] 5.5
$median
[1] 5.5
$sd
[1] 3.02765
result$mean # 单独取出均值[1] 5.5
result$sd # 单独取出标准差[1] 3.02765
👉 $ 符号用于从 list 中取出单个元素。
写一个 `is_even()` 函数,判断一个数是不是偶数,返回 `TRUE` 或 `FALSE`。提示:用 `%%` 取余数。# 3-1 判断偶数
is_even <- function(x) {
x %% 2 == 0
}
is_even(4) # TRUE[1] TRUE
is_even(7) # FALSE[1] FALSE
is_even(1:10) # 对向量也有效 [1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE
当用户传入了不合法的参数时,函数应该报错提醒,而不是悄悄算出错误结果。
safe_log <- function(x) {
if (!is.numeric(x)) stop("x 必须是数值类型")
if (x <= 0) stop("x 必须是正数")
log(x)
}
safe_log(10) # 正常计算[1] 2.302585
# safe_log(-5) # 报错:x 必须是正数
# safe_log("a") # 报错:x 必须是数值类型👉 stop() 就像紧急刹车。一旦执行到它,函数立刻停止,并打印红色报错信息。这样可以”早发现、早解决”,防止错误数据悄悄传播下去。
写一个 `safe_sqrt()` 函数,如果输入是负数,用 `stop()` 报错:"不能对负数开平方根"#安全开根号
safe_sqrt <- function(x) {
if (x < 0) stop("不能对负数开平方根")
sqrt(x)
}
safe_sqrt(9) # 3[1] 3
# safe_sqrt(-4) # 报错R 中有些函数是用 C 语言直接写的,运行速度非常快。可以用 is.primitive() 检查:
sum # 查看 sum 函数function (..., na.rm = FALSE) .Primitive("sum")
is.primitive(sum) # TRUE —— 底层 C 语言编写[1] TRUE
meanfunction (x, ...)
UseMethod("mean")
<bytecode: 0x0000021c68b33038>
<environment: namespace:base>
is.primitive(mean) # FALSE —— 用 R 语言编写[1] FALSE
常见的原函数:sum、+、-、*、/、c、[ 等。它们在 R 启动时就已经加载好了。
不需要给函数取名字,可以直接在调用处定义。常用在 integrate()、sapply() 等函数中:
# 不给函数取名,直接写在 integrate() 里面
integrate(function(x) sin(x)^2, 0, pi)1.570796 with absolute error < 1.7e-14
等价于:
f <- function(x) sin(x)^2
integrate(f, 0, pi)1.570796 with absolute error < 1.7e-14
👉 匿名函数适合只用一次的情况,没必要单独命名。
| 错误 | 原因 | 正确做法 |
|---|---|---|
调用时忘记写 () |
只写了函数名 | say_hello() 而不是 say_hello |
| 参数顺序搞反 | 按位置传参时搞错对应关系 | 用参数名:divide(y=2, x=10) |
| 做了计算但没有返回 | 结果不是最后一个表达式,也没用 return() |
结果放最后一行,或用 return(结果) |
| 大括号不匹配 | 少写或多写 { 或 } |
RStudio 中按 Ctrl + I 自动对齐检查 |
| 忘记函数只能返回一个值 | 写了多个独立的值 | 用 list() 打包:list(a=1, b=2) |
函数名 <- function(参数) { 函数体 }return();多个值用 list() 打包stop() 报错并停止;try() 尝试执行不中断function(x) { ... },不必取名sum、+ 等底层函数,运行速度快👉 学函数的关键不是看懂,而是写熟。 每学一个知识点,自己打开 RStudio 动手写一遍。