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函数族笔记



You must enable javascript to see captcha here!

Copyright © All Rights Reserved · Green Hope Theme by Sivan & schiy · Proudly powered by WordPress

无觅相关文章插件,快速提升流量