如何編寫R函數

轉載自http://blog.sciencenet.cn/blog-255662-501317.html

R語言實際上是函數的集合,用戶可以使用base,stats等包中的基本函數,也可以自己編寫函數完成一定的功能。但是初學者往往認爲編寫R函數十分困難,或者難以理解。這裏對如何編寫R函數進行簡要的介紹。

函數是對一些程序語句的封裝。換句話說,編寫函數,可以減少人們對重複代碼書寫,從而讓R腳本程序更爲簡潔,高效。同時也增加了可讀性。一個函數往往完成一項特定的功能。例如,求標準差sd,求平均值,求生物多樣性指數等。R數據分析,就是依靠調用各種函數來完成的。但是編寫函數也不是輕而易舉就能完成的,需要首先經過大量的編程訓練。特別是對R中數據的類型,邏輯判別、下標、循環等內容有一定了解之後,纔好開始編寫函數。 對於初學者來說,最好的方法就是研究現有的R函數。因爲R程序包都是開源的,所有代碼可見。研究現有的R函數能夠使編程水平迅速提高。

R函數無需首先聲明變量的類型,大部分情況下不需要進行初始化。一個完整的R函數,需要包括函數名稱,函數聲明,函數參數以及函數體幾部分。

1. 函數名稱

即要編寫的函數名稱,這一名稱就作爲將來調用R函數的依據。

2. 函數聲明

包括 <- function, 即聲明該對象的類型爲函數。

3. 函數參數

這裏是輸入的數據,函數參數是一個虛擬出來的一個對象。函數參數所等於的數據,就是在函數體內部將要處理的值,或者對應的數據類型。 函數體內部的程序語句進行數據處理,就是對參數的值進行處理 ,這種處理只在調用函數的時候纔會發生。函數的參數可以有多種類型。R help的界面對每個函數,及其參數的意義及所需的數據類型都進行了說明。

4. 函數體

常常包括三部分。

1. 異常處理

輸入的數據不能滿足函數計算的要求,或者類型不符, 這時候一定要設計相應的機制告訴用戶,輸入的數據在什麼地方有錯誤。 錯誤又分爲兩種。

(1)第一種, 如果輸入的數據錯誤不是很嚴重,可以經過轉換,變爲符合處理要求的數據時, 此時只需要給用戶一個提醒,告知數據類型不符,但是函數本身已經進行了相應的轉換。

(2)第二種,數據完全不符合要求,這種情況下,就要終止函數的運行,而告知因爲什麼,函數不能運行。這樣,用戶在 使用函數的情況先才不至於茫然。

2. 運算過程

包括具體的運算步驟。 運算過程和該函數要完成的功能有關。

R運算過程中,應該儘量減少循環的使用,特別是嵌套循環。R提供了 apply,replicate等一系列函數,來代替循環,應該儘量應用這些函數, 提高效率。 如果在R中實在太慢,那麼核心部分只能依靠C或者Fortran 等語言編寫,然後再用R調用這些編譯好的模塊,達到更高的效率。

運算過程中,需要大量用到if等條件作爲判別的標準。if和while都是需要數據TRUE/FALSE這樣的邏輯類型變量,這就意味着,if內部,往往是對條件的判別,例如 is.na, is.matrix, is.numeric等等,或者對大小的比較,如,if(x > 0), if(x == 1), if(length(x)== 3)等等。if後面,如果是1行,則花括號可以省略,否則就必須要將所有的語句都放在花括號中。這和循環是一致的。

例子1

##if與條件判斷

fun.test <- function(a, b, method=c("add", "subtract")){
  if(method == "add"){
    res <- a + b
  }
  if(method == "substract"){
    res <- a - b
  }
  return(res)
}

##檢驗結果
fun.test(a = 10, b = 8, method = "add")
fun.test(a = 10, b = 8, method = "substract")

> fun.test(a = 10, b = 8, method = "add")
[1] 18
> fun.test(a = 10, b = 8, method = "substract")
[1] 2

#如果if或者for/while等後面的語句只有一行,則無需使用花括號。
#for循環有些時候是必須要用到的,for循環內部,往往需要用下標,訪問數據內的一定元素,例如向量內的元素,這時候用方括號表示。
例子2 for循環與算法
test.sum <- function(x){
  res <- 0
  for(i in 1:length(x)){
    res <- res + x[i]
  }
  return(res)
}
a <- c(1,2,1,6,1,8,9,8)
test.sum(a)
sum(a)
3. 返回值。

返回值就是函數給出的結果。打個比方,編寫一個函數,就像自己攢一個機器,例如現在攢好 一臺豆漿機,該豆漿機要求輸入大豆,輸入的大豆就是參數, 返回的結果,就是豆漿。如果該豆漿機需要不停地輸入大豆, 而不能產出豆漿,這樣的機器就一定會被扔掉。函數也是一樣的, 需要給出返回值。 R中默認的情況是將最後一句作爲返回值。但是爲了函數的可讀性起見,應該儘量指名返回值。返回值用return()函數給出。 函數在內部處理過程中,一旦遇到return(),就會終止運行, 將return()內的數據作爲函數處理的結果給出。

例3 計算標準差

sd2 <- function(x){
  # 異常處理,當輸入的數據不是數值類型時報錯
  if(!is.numeric(x)){
    stop("the input data must be numeric!n") 
  }
  # 異常處理,當僅輸入一個數據的時候,告知不能計算標準差
  if(length(x) == 1){
    stop("can not compute sd for one number,a numeric vector required.n")  
  }
  x2 <- c()
  meanx <- mean(x)
  for(i in 1:length(x)){
    xn <- x[i]-meanx
    x2[i] <- xn^2
    sum2 <- sum(x2)
    sd <- sqrt(sum2/(length(x)-1))
    return(sd)
  }
}

## 程序的檢驗

## 正常的情況
> sd2(c(2,6,4,9,12))
[1] 2.3

## 一個數值的情況
> sd2(3)
Error in sd2(3) : 
  can not compute sd for one number,a numeric vector required.n

## 輸入數據不爲數值類型時
> sd2(c("1", "2"))
Error in sd2(c("1", "2")) : the input data must be numeric!n

再有就是編寫R函數時一定要注意縮進,編輯器用Notepad++, TinnR, Rstudio等,同時用等距字體(如Consolas, Courier new等)和語法高亮顯示。這樣便於快速尋找到其中的錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章