打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
R学习:R for Data Science(七)函数

R学习往期回顾:

R学习:R for Data Science(六)使用forcats处理因子

R学习 从Tidyverse学起,入门R语言 dplyr合并数据

R学习 流程控制 if,else,ifelse

R学习 从Tidyverse学起,入门R语言(tidyr和stringr)

R学习 从Tidyverse学起,入门R语言(tibble,readr和dplyr)

R学习:字符串

R学习:环境和函数

R学习:数据框的基本操作

R学习:R for Data Science(五)

R学习:R for Data Science(四)

R学习:R for Data Science(三)

R学习:R for Data Science(二)

R学习:R for Data Science(一)

与复制粘贴相比,函数可以通过更强大、更通用的方式来自动执行常用任务。相比于复制粘贴,函数具有以下3 个主要优点。· 你可以给函数起一个意味深长的名字,从而让代码更容易理解。· 如果需求发生了变化,只需要修改一处代码即可,无须修改多处。· 消除了复制粘贴时可能出现的无心之失(比如,修改了一处的变量名称,但却没有修改另一处)。

什么时候应该使用函数
只要一段代码需要复制粘贴的次数超过两次(也就是说,同一段代码至少有 3 个副本),那么就应该考虑编写一个函数。

例如

df <- tibble::tibble(a = rnorm(10),b = rnorm(10),c = rnorm(10),d = rnorm(10))df$a <- (df$a - min(df$a, na.rm = TRUE)) /(max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))df$b <- (df$b - min(df$b, na.rm = TRUE)) /(max(df$b, na.rm = TRUE) - min(df$a, na.rm = TRUE))df$c <- (df$c - min(df$c, na.rm = TRUE)) /(max(df$c, na.rm = TRUE) - min(df$c, na.rm = TRUE))df$d <- (df$d - min(df$d, na.rm = TRUE)) /(max(df$d, na.rm = TRUE) - min(df$d, na.rm = TRUE))

这段代码的作用是将每列的值调整到 0 1 之间。
这里犯了一个错误:忘记将其中的一个a 改成 b 了。提取重复代码,将其转换为函数是一种非常好的做法,因为这样可以防止此类错误的发生。

要想编写一个函数,首先需要分析代码,比如,函数需要多少个输入?

(df$a - min(df$a, na.rm = TRUE)) /(max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))

这段代码只有一个输入:df$a
为了让输入更加清晰,应该使用具有通用名称的临时变量来重写代码。
以上代码只需要一个数值向量,我们可以称其为
x

x <- df$a(x - min(x, na.rm = TRUE)) / (max(x, na.rm = TRUE) - min(x, na.rm = TRUE))

这段代码中还有一些重复,我们计算了 3 次数据最大值和最小值,但这可以一步完成

rng <- range(x, na.rm = TRUE)(x - rng[1]) / (rng[2] - rng[1])

将中间计算结果保存为命名变量是一种非常好的做法,因为这样可以让代码的意义更加清楚。既然我们已经简化了代码,并检验了代码可以正常运行,接下来就可以将其转换为函数了:(将0,5,10转换为0-1)

rescale01 <- function(x) { rng <- range(x, na.rm = TRUE) (x - rng[1]) / (rng[2] - rng[1])}rescale01(c(0, 5, 10))

要想创建一个新函数,需要以下 3 个关键步骤 (1) 为函数选择一个名称。在以上示例中,我们使用 rescale01 作为函数名称,因为这个函数的功能是将一个向量调整到 0 到 1 之间。 (2) 列举出 function 中所用的输入,即参数。这个示例中只有一个参数,如果有更多参数,那么函数调用形式就类似于 function(x, y, z)。 (3) 将已经编写好的代码放在函数体中。在 function(...) 后面要紧跟一个用 {} 括起来的代码块。

注意以上创建函数的整体过程。确定函数如何使用简单输入来运行后,我们才开始编写函数。从工作代码开始,再将其转换为函数是相对容易的;先创建函数,再让其正确运行则是比较困难的。

此时我们应该使用其他输入来测试函数是否正确

rescale01(c(-10, 0, 10))#> [1] 0.0 0.5 1.0rescale01(c(1, 2, 3, NA, 5))#> [1] 0.00 0.25 0.50 NA 1.00

既然已经有了函数,那么我们就可以利用它来简化原来的示例了

df$a <- rescale01(df$a)df$b <- rescale01(df$b)df$c <- rescale01(df$c)df$d <- rescale01(df$d)

相对于原来的代码,这段代码更清楚易懂,而且还消除了复制粘贴可能带来的错误。

函数的另一个优点是,如果需求发生变化,我们只需要在一处进行修改。例如,我们发现,如果有些变量中包括无限值,那么 rescale01() 函数就会出错

x <- c(1:10, Inf)rescale01(x)#> [1] 0 0 0 0 0 0 0 0 0 0 NaN

因为已经将代码放在函数中了,所以我们只需要在函数中进行修改即可

rescale01 <- function(x) { rng <- range(x, na.rm = TRUE, finite = TRUE) (x - rng[1]) / (rng[2] - rng[1])}rescale01(x)#> [1] 0.000 0.111 0.222 0.333 0.444 0.556 0.667 0.778 0.889#> [10] 1.000 Inf

函数参数

函数的参数通常分为两大类:一类提供需要进行计算的数据,另一类控制计算过程的细节。举例如下。 · 在 log() 函数中,数据是 x,细节则是对数的底,即 base。 · 在 mean() 函数中,数据是 x,细节则是从 x 前后两端(trim)移除多大比例的数据,以及如何处理缺失值(na.rm)。 · 在 t.test() 函数中,数据是 x 和 y,检验的细节则是 alternative、 mu、 paired、 var.equal 以及 conf.level 等设置。 · 在 str_c() 函数中,你可以向 ... 参数提供任意数量的字符串作为数据,连接的细节则由 sep 和 collapse 参数控制。

通常情况下,数据参数应该放在最前面,细节参数则放在后面,而且一般都有默认值。设置默认值的方式与使用命名参数调用函数的方式是一样的

使用近似正态分布计算均值两端的置信区间

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)#> [1] 0.498 0.610mean_ci(x, conf = 0.99)#> [1] 0.480 0.628

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

在调用函数时,我们经常省略数据参数的名称,因为其使用太普遍了。如果不想使用细节参数的默认值,那么你应该使用细节参数的完整名称

# 好mean(1:10, na.rm = TRUE)# 不好mean(x = 1:10, , FALSE)mean(, TRUE, x = c(1:10, NA))

如果一个参数名称的前几个字母可以唯一标识这个参数,那么你可以通过这些字母来引用这个参数,如 mean(x, n = TRUE)。但是,通常要在不会引起混淆的情况下才能这样做。注意,在调用函数时,应该在其中 = 的两端都加一个空格。逗号后面应该总是加一个空格,逗号前面则不要加空格(与英文写法相同)。使用空格可以使得函数的重要部分更易读

# 好average <- mean(feet / 12 + inches, na.rm = TRUE)# 不好average<-mean(feet/12+inches,na.rm=TRUE)

点点点(...)
R 中的很多函数可以接受任意数量的输入

sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)#> [1] 55stringr::str_c("a", "b", "c", "d", "e", "f")#> [1] "abcdef

这种函数是如何运行的呢?它们需要一个特殊参数:...(读作点点点)。这个特殊参数会捕获任意数量的未匹配参数。

这个参数的作用非常大,因为你可以将它捕获的值传给另一个函数。如果你的函数是另一个函数的包装器,那么这种一网打尽的方式就非常有用了。例如,我们经常用以下方式创建辅助函数来包装 str_c() 函数:

commas <- function(...) stringr::str_c(..., collapse = ", ")commas(letters[1:10])#> [1] "a, b, c, d, e, f, g, h, i, j"rule <- function(..., pad = "-") { title <- paste0(...) width <- getOption("width") - nchar(title) - 5 cat(title, " ", stringr::str_dup(pad, width), "\n", sep = "")}rule("Important output")

这里 ... 可以将我们不想处理的所有参数传递给 str_c()

公众号“生信小课堂”

TCGA数据分析大全


    

零代码1-2分SCI生信分析教学

                                                      

                                                                                

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
R数据分析之merge函数
R数据科学--第三章dplyr
R语言学习
R for Data Science(十三)
R语言学习----数据缺失值,表整理
《R数据科学》第3章——dplyr是什么
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服