1. 程式人生 > >R學習筆記 第四篇:函數,分支和循環

R學習筆記 第四篇:函數,分支和循環

匿名 操作數 play 控制 als layers null 操作 str

變量用於臨時存儲數據,而函數用於操作數據,實現代碼的重復使用。在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

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,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)用於查看函數調用的函數。

二,分支和循環

分支和循環是通用編程必不可少的兩大流程控制元素,分支語句根據條件,執行不同的代碼,而循環語句重復執行代碼塊,可以根據條件結束循環。

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

四,分組聚合

在研究數據時,有時需要對數據按照特定的字段進行分組,然後統計各個分組的數據,這就是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學習筆記 第四篇:函數,分支和循環