R学习笔记 第四篇:函数,分支和循环

简介:

变量用于临时存储数据,而函数用于操作数据,实现代码的重复使用。在R中,函数只是另一种数据类型的变量,可以被分配,操作,甚至把函数作为参数传递给其他函数。分支控制和循环控制,和通用编程语言的风格很相似,但是,不要因为R具有这些元素,就把R作为通用编程语言来看待,R的最小变量是向量,是一种面向数组(Array-Oriented)的语言。在编程时,尽量用array的方式思考,避免使用循环(for,while,repeat)控制,而使用apply函数家族实现计算的迭代,这是R语言的特色,把特定的函数应用于向量,列表或数组中的每个元素上。

一,R风格的函数

R通过function关键字定义函数,函数主要由函数名称,参数,运行的代码块和返回值组成,函数名称是变量,参数是调用函数时需要传递的形式参数;代码块是由由大括号构成,是调用函数时需要执行的代码逻辑;R的函数不需要显式地使用return关键字明确返回值,R函数的计算的最后一个值将自动作为返回值。

1,创建函数

例如,创建一个函数,并把函数赋值给avg变量,可以认为函数的名称是avg,其有两个参数a和b,大括号内的代码是函数体,实现的功能是求两个数的平均值。在R中创建函数时,如果函数体只有一行代码,可以省略大括号。

avg=function(a,b)
{
    mead(c(a,b))
}

该函数没有显式调用return关键字,函数代码块种计算的最后一次自动最为返回值,在avg函数种,代码块调用系统函数mean,返回参数a和b的平均值,调用格式是函数名称(形式参数):

> avg(2,3)
[1] 2.5

调用函数时,如果不命名参数,则R按照位置匹配参数,因此,2对应形式参数a,3对应形式参数b。如果要改变传递参数的顺序,则可以传入命名参数:

> avg(b=2,a=3)
[1] 2.5

可以为函数的参数设置默认值,当调用函数时,如果没有为参数传递相应的值,那么函数将自动使用默认值作为函数代码块执行的当前值。

> avg=function(a=1,b=1)mean(c(a,b))
> avg()
[1] 1

虽然函数中最后一行代码的值是自动返回的,但是,为了便于阅读代码,建议使用return函数,清晰地指定要返回的值,return函数除了指定函数的返回值之外,还能使函数退出,不再继续执行后面的代码。

> avg=function(a=1,b=1) return(mean(c(a,b)))

2,把函数作为参数传递给其他函数

函数本质上是一个函数类型的变量,和其他类型的变量一样,能够作为函数的形式参数,传递给其他函数,例如:

> fn_call=function(x,a,b)x(a,b)
> fn_call(avg,1,2)
[1] 1.5

R提供一个系统函数do.call(fun,args),用于调用其他函数,第一个参数是函数变量,第二个参数是有参数构成的列表:

> do.call(avg,args=list(1,2))
[1] 1.5

3,查看函数的定义

通过函数args用于显示函数的头部文本,用于显示函数的头部定义,例如:

> args(avg)
function (a = 1, b = 1) 
NULL

body(fun)函数用于返回函数的代码块,例如:

> body(avg)
mean(c(a, b))

除了这两个函数,还有函数formals(fun),用于查看函数的参数及其默认值,如果函数没有定义参数的默认值,那么只显示参数名,formals显示的参数名格式是$参数名。函数deparse(fun)用于查看函数调用的函数。

4,特殊的参数

R提供了一个特殊的运算符(...),该运算符允许函数具有任意多个的参数,并且不需要在函数定义中指定。

avg=function(a,b, ...) mead(c(a,b))

二,分支和循环

分支和循环是通用编程必不可少的两大流程控制元素,分支语句根据条件,执行不同的代码,而循环语句重复执行代码块,可以根据条件结束循环。

1,分支控制

经典流程控制关键字是if-else,特殊的关键字ifelse(test, yes, no),当test条件为TRUE时,返回yes值,当test条件为FALSE时,返回no值。

> ifelse(c(1,2,3)>=2,'Greater','Less')
[1] "Less"    "Greater" "Greater"

如果分支较多,可以使用switch函数实现分支的选择,switch函数的第一个参数是表达式(exp),通常是一个字符串;当表达式(exp)匹配后续的参数名(即变量名)时,返回参数的值:

> color=function(t) switch(t,r='red',g='green',b='blue')
> color('r')
[1] "red"

如果不匹配任何参数名,switch函数不返回任何值,可以添加一个匿名的参数,当表达式(exp)匹配不上任意一个命名参数时,switch函数将返回匿名参数的值:

> color=function(t) switch(t,r='red',g='green',b='blue', 'error')
> color('d')
[1] "error"

2,循环控制

循环控制语句是repeat、while和for,R的向量化自带循环特性,减少了循环语句使用的场景,但是在重复执行代码时,循环控制还是非常有用。

repeat 循环:先执行代码,遇到break关键字,结束循环,也可以在break关键字前增减if(test)语句,当指定的条件成立(为TRUE)时,执行break关键字,结束循环:

repeat {
code
if(test) break
}

while 循环:先检测条件,如果条件为TRUE,执行code;如果条件为FALSE,结束循环:

while(test)
{
code
}

for 循环:使用迭代器和一个向量参数,在每个循环中,迭代器变量从向量中取得一个值,直到迭代所有得向量:

for(i in c(1:5))
{
code
}

三,高级循环控制

高级循环控制主要是指编写向量化的代码,apply系列的函数使得循环更加向量化,更加高效。

1,重复调用表达式

rep函数把输入的参数重复多次,如果参数是表达式,rep函数会把表达式的结果重复多次;而replicate函数是重复调用表达式,每次调用表达式的过程都是独立的,这意味着,如果产生随机数,rep函数产生的随机数是”伪随机的“,重复第一次产生的随机数,而replicate产生的随机数是真正随机的,每次都不相同。

> rep(runif(1),5)
[1] 0.8721105 0.8721105 0.8721105 0.8721105 0.8721105
> replicate(5,runif(1))
[1] 0.9426709 0.1280271 0.1926333 0.7091503 0.5404846

2,遍历数组

R 文档中,apply函数返作用于数组或矩阵,按照相应的维度(边,margin)提取参数,调用函数执行计算,并返回的结果,结果的类型是向量,数组,或列表。

Returns a vector or array or list of values obtained by applying a function to margins of an array or matrix.

apply函数的最大特色是用于调用函数,第一个参数是数组或矩阵,第二个参数是边,即维度的序号或维度名称,1是指按照第一个维度(row),2是指按照第二个维度(column),3是指按照第三个维度(high),以此类推,也可以是维度的向量,例如,c(1,2);第三个参数是函数变量,函数变量必须能够接收相应维度的所有项:

apply(X, MARGIN, FUN, ...)

例如,对矩阵按照行(row)调用求和函数,统计每行的加和,即把同一行中的所有列的值相加求和:

复制代码
> a_matrix=matrix(data=1:12,nrow=4)
> a_matrix
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> apply(a_matrix,1,sum)
[1] 15 18 21 24
复制代码

2,遍历列表

遍历列表是指遍历列表的所有列表项,最常用的apply函数是lapply,该函数名是”list apply“的缩写,该函数的定义是:lapply(x, FUN, ...),参数x是列表变量,参数FUN是应用在列表项上的函数,lapply函数返回的结果是一个列表,其长度和参数x(参数是一个列表)的长度相同,每一个列表项对应参数x的列表项,只不过,lapply函数把参数FUN指定的函数,作用于参数x的每个列表项,把FUN处理的结果,作为新的列表项返回。

例如,定义一个列表,该列表有两个列表项,每个列表项中都有重复的元素值:

> a_list=list(c('a','b','a'),rep(c(1:2),5))
> a_list
[[1]]
[1] "a" "b" "a"

[[2]]
 [1] 1 2 1 2 1 2 1 2 1 2
View Code

方法1,通过for循环,逐个遍历列表项,使用unique函数,去除列表项中元素的重复值:

> b_list=list()
> for(i in seq_along(a_list)){
+     b_list[[i]]=unique(a_list[[i]]);
+ }

方法2,使用lapply函数,应用unique函数,去除列表项中元素的重复值:

> lapply(a_list,unique)

3,多参数列表应用

mapply是多参数列表应用(multiple argument list apply)的简称,mapply函数的第一个参数是函数变量(fun),函数变量可以接收多个向量作为参数。mapply函数的作用是把函数变量应用于参数的各个元素。

> va=c(1:5)
> vb=c(6:10)
> mapply(sum,va,vb)
[1]  7  9 11 13 15

4,分组聚合

apply函数家族,最重要的函数是tapply,在研究数据时,有时需要对数据按照特定的字段进行分组,然后统计各个分组的数据,这就是SQL语法中的分组聚合。在R语言中,可以通过三步实现:拆分-应用-合并(Split-Apply-Combine)

对玩家的游戏成绩进行统计和分析,创建示例数据:

> players_scores=data.frame(
+     player=rep(c('Tom','Dick','Jim'),times=c(2,5,3)),
+     score=round(runif(10,1,100),-1)
+ )

计算每个玩家的平均得分,首先对玩家分组,split函数的作用是按照特定的字段对数据框进行分组,第一个参数是数据框对象,第二个参数是分组字段,split函数返回的结果是列表对象。

例如,split(score,player)函数的作用是按照player字段把数据框中的score拆分成一组,也就是说,player 相同的score是同一个分组,填充到同一个列表项中:

复制代码
> (scores_by_player=with(players_scores,split(score,player)))
$Dick
[1] 70 20 30 70 70

$Jim
[1] 80 90 50

$Tom
[1] 80 90
复制代码

第二步是对每个分组计算平均分,利用lapply函数,把函数引用于列表的每个列表项中:

list_mean_by_player=lapply(scores_by_player,mean)

第三步是把结果合并到单个向量中,也就是把列表转换成向量,

> unlist(list_mean_by_player)
    Dick      Jim      Tom 
52.00000 73.33333 85.00000 

在数据分析中,”拆分-应用-合并“ 显示十分繁琐,tapply函数一次完成所有的三个步骤,一气呵成:

with(players_scores,tapply(score,player,mean))

tapply函数常用的参数共有三个,第一个参数是:数据框对象或向量,第二个参数是因子列表,也就是分组字段,第三个参数是指对单个分组应用的函数变量:

tapply(X, INDEX, FUN = NULL, ...)

by函数和aggregate函数是tapply函数的包装函数,功能相同,接口稍微不同。

 

参考文档:

R Document

R语言apply函数族笔记

作者悦光阴
本文版权归作者和博客园所有,欢迎转载,但未经作者同意,必须保留此段声明,且在文章页面醒目位置显示原文连接,否则保留追究法律责任的权利。
分类: R
标签: R

本文转自悦光阴博客园博客,原文链接:http://www.cnblogs.com/ljhdo/p/5159900.html,如需转载请自行联系原作者
目录
相关文章
|
7天前
|
C语言
爱上C语言:分支与循环(分支篇)多个if与if — else if区别
爱上C语言:分支与循环(分支篇)多个if与if — else if区别
|
5月前
|
机器学习/深度学习 编译器 C++
C++模板元模板实战书籍讲解第一章(顺序、分支与循环代码的编写)--续篇
C++模板元模板实战书籍讲解第一章(顺序、分支与循环代码的编写)--续篇
50 0
|
5月前
|
机器学习/深度学习 编译器 C++
C++模板元模板实战书籍讲解第一章(顺序、分支与循环代码的编写)--前篇
C++模板元模板实战书籍讲解第一章(顺序、分支与循环代码的编写)--前篇
34 0
|
4天前
|
Python
[重学Python] Day1 变量+分支+循环
[重学Python] Day1 变量+分支+循环
23 3
|
15天前
|
C++
C++ While 和 For 循环:流程控制全解析
本文介绍了C++中的`switch`语句和循环结构。`switch`语句根据表达式的值执行匹配的代码块,可以使用`break`终止执行并跳出`switch`。`default`关键字用于处理没有匹配`case`的情况。接着,文章讲述了三种类型的循环:`while`循环在条件满足时执行代码,`do/while`至少执行一次代码再检查条件,`for`循环适用于已知循环次数的情况。`for`循环包含初始化、条件和递增三个部分。此外,还提到了嵌套循环和C++11引入的`foreach`循环,用于遍历数组元素。最后,鼓励读者关注微信公众号`Let us Coding`获取更多内容。
19 0
|
15天前
|
机器学习/深度学习 C语言
【C语言】分支循环第二章 1
【C语言】分支循环第二章
|
1月前
|
机器学习/深度学习 程序员 编译器
c语言从入门到实战——分支和循环
C语言是结构化的程序设计语言,这里的结构指的是顺序结构、选择结构、循环结构,C语言是能够实 现这三种结构的,其实我们如果仔细分析,我们日常所见的事情都可以拆分为这三种结构或者这三种结构的组合。 我们可以使用 if 、 switch 实现分支结构,使用 for 、 while 、 do while 实现循环结构。
76 0
c语言从入门到实战——分支和循环
|
9月前
|
C语言
分支和循环习题以及知识点
分支和循环习题以及知识点
|
10月前
|
C语言
第四章:C语言的分支与循环
说起分支与循环呀,其实生活中充满着分支与循环,比如说当我们面临一个问题与抉择时,我们会选择,会做出规律性的生活方式。人生处处有选择,会有不同的道路,选择自己合适的道路,并保持下去。而C语言也有相关的分支与循环,如 if,if-else,switch,for循环,while循环,do-while循环,今天我们所要讲的是C语言:分支与循环。
39 0
|
编译器 C语言
『C语言从入门到进阶』第 ① 期 - 分支语句
『C语言从入门到进阶』第 ① 期 - 分支语句
61 0