R语言基础系列:

  • 1数据类型(向量、数组、矩阵、 列表和数据框)

  • 2读写数据所需的主要函数、与外部环境交互

  • 3数据筛选——提取对象的子集

  • 4向量、矩阵的数学运算

  • 5控制结构

Your first R function

命令一个函数时,一般不写在交互式命令行里,而是写在一个空白的文本文件中,即把函数放在一个R包中,这里面包含着文档(documentation),是一个更加结构化的环境。

我们使用的软件是RStudio,左上角新建一个新的R脚本来写代码。

Example 1

举个例子,简单了解一下如何使用函数的语法、如何指定参数以及如何返回结果:

命令一个简单的函数,求x, y的和。

## 给函数赋值为add2,第一行小括号里写进变量,第二行大括号里写入运算
add2 <- function(x, y) {x + y
}
## 因为R函数会返回最后一个表达式的值,所以不用写返回

在控制台(console)运行脚本,赋值后即可得到结果:

> add2 <- function(x, y) {
+   x + y
+ }
> add2(7, 5)
[1] 12

Example 2

下一个例子稍微复杂一点:

我们要输入一个数字向量,然后返回这个向量的子集(返回其中大于10的数字)

above10 <- function(x){   ##  命名变量use <- x > 10         ##  逻辑语句,来判断变量x是否大于10x[use]                ##  取子集
}

运行:

> above10 <- function(x){
+   use <- x > 10
+   x[use]
+ }
> x <- 1:16
> above10(x)
[1] 11 12 13 14 15 16
> above10(1:9)
integer(0)

如果我们不设置10,而是改成任意数字n,以上命令可改写为:

above <- function(x, n){use <- x > n x[use]
}

运行:

> above <- function(x, n){
+   use <- x > n
+   x[use]
+ }
> x <- 1:16
> above(x, 12)
[1] 13 14 15 16

设置“缺省值”(可以理解为默认值),即如果在函数运行过程中不指定n的值时,系统自动筛选的标准:

above <- function(x, n = 10){   ## 设置缺省值为10use <- x > n x[use]
}

这时当你运行函数时:

> above <- function(x, n = 10){
+   use <- x > n
+   x[use]
+ }
> above(x)     ## 自动筛选数字向量中大于10的数字[1] 11 12 13 14 15 16

Example 3

下面这个例子再复杂一点,我们要给函数一个参数,然后使用循环遍历这个函数的每一列:

比如,取一个矩阵,然后计算每列的平均值:

columnmean <- function(x){   ## 给函数命名,设置参数x,用于储存矩阵nc <- ncol(x)              ## 计算矩阵中有多少列means <- numeric(nc)       ## 设置数值向量储存列数,长度等于列数,它是一个空向量,每个元素的初始值为0,数值在循环中填满。for (i in 1:nc){           ## 设置循环,循环参数在整数向量1到列数之间means[i] <- mean(x[,i])  ## 把每一列的平均值赋予means[i],x[,i]是矩阵取子集,即求每列的平均值}means                 ## 返回means,平均值向量
}

运行这个程序,计算 airquality数据集每列的平均值:

ps. airquality数据集有6列,前六行长这样:

> head(airquality)Ozone Solar.R Wind Temp Month Day
1    41     190  7.4   67     5   1
2    36     118  8.0   72     5   2
3    12     149 12.6   74     5   3
4    18     313 11.5   62     5   4
5    NA      NA 14.3   56     5   5
6    28      NA 14.9   66     5   6

运行脚本可以看到函数 columnmean(airquality)返回了6个平均值:

> columnmean <- function(x){
+   nc <- ncol(x)
+   means <- numeric(nc)
+   for (i in 1:nc){
+     means[i] <- mean(x[,i])
+   }
+   means
+ }
> columnmean(airquality)
[1]        NA        NA  9.957516 77.882353  6.993464 15.803922

可以看到,如果某列有缺失值NA的话,计算得出的数值就直接是NA。

所以我们可以添加一个逻辑参数,设置移除缺失值:

columnmean <- function(x, removeNA = TRUE){   ## 添加参数,并设置缺省值为TRUEnc <- ncol(x)means <- numeric(nc)for (i in 1:nc){means[i] <- mean(x[,i], na.rm = removeNA)   ## 在mean()函数中添加参数}means
}

再次运行程序,可以看到计算结果是默认移除NA后求得的平均值:

> columnmean(airquality)
[1]  42.129310 185.931507   9.957516  77.882353   6.993464  15.803922

Functions in R

主要分三个部分来讲解函数:

  • 编写函数所需的基础知识

  • 相关语法作用域

  • R语言作用域的规则

编写函数所需的基础知识

R语言通过 function()指令来命名和创建函数。首先要给函数赋值,也就是命名,然后在小括号中写入参数,最后再大括号中写入函数要执行的语句,其基本语法是:

f <- function(<arguments>){## Do something interesting
}

同时在R中,你可以将函数作为参数传递给其他函数,即嵌套。

函数的返回值是函数执行部分中的最后一行表达式。

编写函数的过程中我们可以设置和命名参数,这些参数可以代表数值、矩阵、数据框或逻辑值等等。同时也可以设置一些具有缺省值(默认值)的参数。

  • 形式参数(formal arguments)

形式参数是包含在函数定义里的参数。

formals()会将一个函数作为输入(input),并返回函数所有的形式参数组成的列表。

在R中,不是所有命令都用到所用的形式参数。加入一个函数中设置了10个参数,但我们往往并不需要指定每个参数的值是啥,所以函数可以缺失某些参数。当没有明确赋值是,它的取值就是缺省值(默认值,default value)

  • 匹配参数(argument matching)

可以根据位置或名称来匹配函数参数,这是编写和调用函数的关键。

以计算数据标准差的函数 sd()为例。

> data <- rnorm(100)    ## 取100个符合正态分布的随机数
> sd(x = data)          ## 给参数赋值 求标准差
[1] 1.035329
> sd(data)              ## 给参数默认赋值
[1] 1.035329
> sd(data, na.rm = FALSE)
[1] 1.035329
> sd(na.rm = FALSE, data)  ## 调换参数位置后结果不变
[1] 1.035329

以上所有表达式都是等价的,但是最好不要调换参数位置。

如果函数中参数较多,那么最好使用位置匹配。

比如 lm()函数(把数据拟合到线性模型),它的参数列表这么长:

> args(lm)
function (formula, data, subset, weights, na.action, method = "qr", model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE, contrasts = NULL, offset, ...)
NULL

前五个参数都没有缺省值,依次是,公式、数据、子集、权重等。这里使用者必须要指定他们的值。

lm(y ~ x, mydata, 1:100, model = FALSE)

大多数情况下,我们不知道参数的具体位置,所以在命令行中,命名参数来匹配最安全。

The order of operations when given an argument is:

  1. Check for exact match for a named argument

  2. Check for a partial match

  3. Check for a positional match

  • 惰性求值(Lazy Evaluation)

惰性求值是R语言的一个关键特性,也是许多编程语言常用的模型。仅在使用函数参数时对其求值。

第一个例子:

> f <- function(a, b) {
+         a^2
+ }
> f(2)
[1] 4

这里定义函数f,有两个参数,但返回值仅仅是a的平方。所以当运行f(2)时,和b无关,所以系统自动跳过,不会报错。

第二个例子:

> f <- function(a, b) {
+         print(a)
+         print(b)
+ }
> f(45)
[1] 45
Error in print(b): argument "b" is missing, with no default

这里同样定义f有两个参数,但返回值是a和b,所以当输入f(45)时,因为第二个位置上缺少b的赋值,所以会报错。这里就是用了惰性求值,即,仅在使用这个参数的时候进行求值,在这之前的程序都是有效的并可以执行,直至运行到出错的部分。

  • 特殊参数 ...

...参数是一种特殊的参数,表明一些可以传递给另一个函数的参数。常用于当你需要扩展另一个函数,而你又不想复制原函数的整个参数列表时。

如下例,你希望修改 plot()函数中的个别参数,而其他参数保持不变,将其应用于一个新定义的函数中 myplot()

myplot <- function(x, y, type = "l", ...) {plot(x, y, type = type, ...)         ## Pass '...' to 'plot' function
}

在泛型函数(generic function)中, ...还有另一种用法,它的作用是根据数据类型使用合适的方法

泛型函数是一个函数族,其中的每个函数都有相似的功能,但是适用于某个特定的类。

> mean
function (x, ...)
UseMethod("mean")
<bytecode: 0x5d5e3e8>
<environment: namespace:base>

还有一种情况下, ...参数必须使用:

那就是,当传递到函数的参数数量不能事先确定的时候。

比如 paste()函数,他的作用是将一连串字符串连接起来,然后新建一个字符串或向量,所以无法预知参数个数:

> args(paste)
function (..., sep = " ", collapse = NULL)
NULL

还有 cat()函数,它的功能是和 paste相似,也是连接字符串。

> args(cat)
function (..., file = "", sep = " ", fill = FALSE, labels = NULL, append = FALSE)
NULL

使用 ...函数的一个注意事项:

就是任何出现在 ...之后的参数列表必须明确的给出名称。而且不能够部分匹配或位置匹配

举例:

> paste("a","b",sep = ":")
[1] "a:b"

不能位置匹配或部分匹配:

> paste("a","b",":")
[1] "a b :"
> paste("a","b",se = ":")
[1] "a b :"

Scoping Rules of R

作用域(scope,或译作有效范围)是名字(name)与实体(entity)的绑定(binding)保持有效的那部分计算机程序。

什么是作用域规则?

作用域规则(Scoping Rules)决定了一个函数的值如何与自变量绑定起来

在一个函数中,有两种类型的变量:

一种是函数的参数,

另一种存在于函数中的其他变量或符号,并非是函数的参数。问题在于你如何给这些符号赋值。

R用的是词法作用域(Lexical Scoping),也成静态作用域。

词法作用域又叫做静态作用域,采用词法作用域的变量叫词法变量。

词法作用域里,取变量的值时,会检查函数定义时的文本环境,捕捉函数定义时对该变量的绑定。

词法变量有一个在编译时静态确定的作用域。词法变量的作用域可以是一个函数或一段代码,该变量在这段代码区域内可见(visibility);在这段区域以外该变量不可见(或无法访问)。

相反,采用动态作用域的变量叫做动态变量。

只要程序正在执行定义了动态变量的代码段,那么在这段时间内,该变量一直存在;代码段执行结束,该变量便消失。

词法作用域的优点是能够简化运算,在统计分析时非常有效

通过下面这个函数,举个栗子:

f <- function(x, y) {x^2 + y / z
}

这个函数是取x的平方然后加上y除以z的值,其中有两个明确的形式参数xy,问题是z从哪儿来的?

因为没有在函数中定义z,所以z是一个自由变量

语法作用域解决的问题就是怎样给一个类似z的自由变量赋值


词法作用域的规则

词法作用域的规则,简而言之一句话:

在定义函数的环境中搜索自由变量的值

那么问题来了……

什么是环境?

环境是符号-值对(symbol-value,如x = 3.14)的集合。每一个符号都有一个与之绑定的值。

每个环境都有一个上层环境(parent environment)。对于上层环境而言,他可能有很多子环境。唯一没有上层环境的环境叫做空环境。

你可以把你的全局环境(工作空间)看做一系列“符号-值对”,其中每个对象都有一个与之关联的对象。

因此,每一个包都有一个命名空间,就是一个环境,其中有很多符号、以及与符号关联的值。

如果你把一个函数和环境联系起来,就创建了一个闭包(function losure),这些闭包是R中各种各样神器操作的关键所在。


所以,如果要在函数里遇到自由变量,怎么办?

先你需要找的是:这个函数是在那个环境中被定义的。看看是否在全局环境中被定义,如果没有,就去它的父环境里面找,以此类推,往上找直到顶层环境。(在全局环境外定义函数也是有可能的)

如果所有环境中都找不到想要的符号的话,就会报错。


作用域规则的重要性

为什么作用域规则很重要?

通常在全局环境重定义一个函数,在工作区就能够找到自由变量的值。

但是重点是,在R中,你能够在函数里面再定义其他函数

一般情况下,函数的返回值是数值、数据框、列表、等等,但也有可能是一个函数

在这种情况下,全局环境就产生了变化,作用域原则的影响就表现出来了。

举例,定义一个“构造性”函数,即这个函数在构造另一个函数:

make.power <- function(n) {pow <- function(x){x^n}pow
}

创建一个构造函数mmake.power(),赋值为n;其内部用来构造另一个函数pow(),赋值为x。这个函数的功能是,pow()对它的参数x求n次方,然后make.power()返回结果。

所以在函数pow()的内部,x是参数,n就是一个自由变量。

所以如果运行函数make.power,赋值给cube,那么cube就会返回一个函数:

> cube <- make.power(3)
> cube
function(x){x^n}
<environment: 0xc8d30e8>

这时,cube就成了一个函数cube(),相当于pow()(实际上是没有pow的,他只是一个内部的代号),所以调用cube()

> cube(2)
[1] 8

(2的立方是8)


Exploring a Function Closure

怎么才能查看一个函数所在的环境中都有啥?

调用ls函数

如上例:

> ls(environment(cute))
[1] "n"   "pow"

查看对象的赋值,使用get()

> get("n", environment(cute))
[1] 3

这就是cute()怎么知道n=3的过程。


参考资料:

  1. 视频课程 R Programming by Johns Hopkins University:https://www.coursera.org/learn/r-programming/home/welcome

  2. 讲义 Programming for Data Science :https://bookdown.org/rdpeng/rprogdatascience/R

-------------------我是求关注的分界线--------------

欢迎大家跟我一起上车~~~~请关注

猜你喜欢

10000+:肠道细菌 人体上的生命 宝宝与猫狗 梅毒狂想曲 提DNA发Nature 实验分析谁对结果影响大  Cell微生物专刊

系列教程:微生物组入门 Biostar 微生物组  宏基因组

专业技能:生信宝典 学术图表 高分文章 不可或缺的人

一文读懂:宏基因组 寄生虫益处 进化树

必备技能:提问 搜索  Endnote

文献阅读 热心肠 SemanticScholar Geenmedical

扩增子分析:图表解读 分析流程 统计绘图

16S功能预测   PICRUSt  FAPROTAX  Bugbase Tax4Fun

在线工具:16S预测培养基 生信绘图

科研经验:云笔记  云协作 公众号

编程模板 Shell  R Perl

生物科普  生命大跃进  细胞暗战 人体奥秘

写在后面

为鼓励读者交流、快速解决科研困难,我们建立了“宏基因组”专业讨论群,目前己有国内外150+ PI,1300+ 一线科研人员加入。参与讨论,获得专业解答,欢迎分享此文至朋友圈,并扫码加主编好友带你入群,务必备注“姓名-单位-研究方向-职称/年级”。技术问题寻求帮助,首先阅读《如何优雅的提问》学习解决问题思路,仍末解决群内讨论,问题不私聊,帮助同行。

学习16S扩增子、宏基因组科研思路和分析实战,关注“宏基因组”

点击阅读原文,跳转最新文章目录阅读

R语言笔记6:在R中写一些简单的函数、functions基础和作用域相关推荐

  1. R语言笔记6:在R中写一些简单的函数

    Your first R function 命令一个函数时,一般不写在交互式命令行里,而是写在一个空白的文本文件中,即把函数放在一个R包中,这里面包含着文档(documentation),是一个更加结 ...

  2. R语言笔记2:读写数据所需的主要函数、与外部环境交互

    R语言基础系列前情提要: - 1数据类型(向量.数组.矩阵. 列表和数据框) Getting Data In and Out of R (一)读取数据 读取数据所需的几种函数: read.table. ...

  3. R语言笔记一:R软件的下载、界面简介、帮助文档

    如何使用R软件求解统计问题? 一.R软件简介 R是一个有着统计分析功能及强大作图功能的软件系统.R既是一种软件也可以说是一种语言. R软件是完全免费的,我们可以通过R软件网站(https://www. ...

  4. R语言笔记1:t检验和Wilcoxon检验

    转自新浪博客,转载地址:http://blog.sina.com.cn/s/blog_427c24ae0102wg7n.html 1.t检验 数据是高血压患者治疗前后舒张压的变化,这个内容最熟悉不过了 ...

  5. R语言笔记——”org.Hs.eg.db“脱坑记录

    R语言笔记--"org.Hs.eg.db"脱坑记录 "org.Hs.eg.db"是发布在bioconductor平台上面的一个数据库文件,该包中装有较多的主流数 ...

  6. R语言计算资本资产定价模型(CAPM)中的Beta值和可视化

    原文链接:http://tecdat.cn/?p=22588 今天我们将计算投资组合收益的CAPM贝塔.这需要拟合一个线性模型,得到可视化,从资产收益的角度考虑我们的结果的意义. 简单的背景介绍,资本 ...

  7. R语言在气象、水文中数据处理及结果分析、绘图

    R语言是一门由统计学家开发的用于统计计算和作图的语言(a Statistic Language developed for Statistic by Statistician),由S语言发展而来,以统 ...

  8. R语言ggplot2可视化:ggplot2中使用element_text函数设置轴标签文本粗体字体(bold text,只设置x轴的标签文本使用粗体字体)

    R语言ggplot2可视化:ggplot2中使用element_text函数设置轴标签文本粗体字体(bold text,只设置x轴的标签文本使用粗体字体) 目录

  9. R语言ggplot2在可视化图像中添加横线并在横线中添加文本、为横线中添加的文本添加文本框、自定义文本框的填充色(background color for a text annotation)

    R语言ggplot2在可视化图像中添加横线并在横线中添加文本.为横线中添加的文本添加文本框.自定义文本框的填充色(background color for a text annotation) 目录

最新文章

  1. 【组队学习】【25期】Datawhale组队学习内容介绍
  2. 招聘|腾讯机器人实验室语义视觉方向(实习+社招)
  3. win10如何使用pip下载适合自己python版本的matplotlib?(亲测有效)
  4. python封装sql脚本 github_Github 大牛封装 Python 代码,实现自动发送邮件只需三行代码...
  5. JVM指令:invokeSpecial/invokeVirtual/invokeStatic/invokeInterface/invokeDynamic方法调用指令
  6. android 文件选择器_Android 开发 打开系统文件、图片、视频等 实现单选多选功能...
  7. 知识(文章)付费阅读系统源码(含小程序)
  8. 华为Python面试题
  9. 9-21 调试javaweb 数据库连接感想
  10. 第21章 java线程(1)-线程初步
  11. 智能优化算法之海豚回声定位(Dolphin echolocation,DE)
  12. 【浙江省第16届省赛E:】Sequence in the Pocket(思维--不模拟复杂过程)
  13. php web长时间不操作退出,Ecshop管理员登陆后台后短时间不操作自动退出的解决方法...
  14. android 摄像头检测工具,检摄app2.0.2最新版(摄像头检测)
  15. 2021计算机excel,excel2021版本
  16. 山东大学网络靶场实验平台—团队进度(三)
  17. Java中的过滤器doFilter里的chain.doFilter()函数理解
  18. IJPay微信退款协议不正确 No appropriate protocol
  19. 手机闪存速度排行_真机闪存实测:闪存读取速度到底什么鬼差距有多大
  20. oca考试及ocp考试指南

热门文章

  1. Docker是世界上最牛逼的CaaS!
  2. 由优劣语言之争引起的思考
  3. 有哪些可以免登录的视频会议软件/服务?
  4. Linux常用命令及技巧3
  5. java构造方法的书写和注意事项(入门可看)
  6. 17Mediator(中介者)模式
  7. C#BindingSource的DataSource的注意点
  8. python获利模式_Python 分段利润提成
  9. 15个目标检测开源数据集汇总
  10. MULLS:一种基于多尺度线性最小二乘的激光SLAM算法