Loop functions

R內建的語法糖,讓使用者能夠用一行就執行某些迴圈才能完成的工作,畢竟如果在終端機上下指令,要打一堆括號不太方便麻...


lapply(X, FUN, ...)與sapply(X, FUN, ...)

這兩個快樂夥伴基本上差不多,對一個list內所有的元素套用函式FUN做處理,再把結果回傳,差別在於lapply總是回傳list,而sapply會嘗試以元素的長度判斷要回傳的資料是vector(元素長度都是1)或matrix(元素長度都相等),如果無法判斷(元素長度不等)才會回傳list,參數說明如下:

  • X 要被處裡的list
  • FUN 要用來處理X內元素的函式,可以是函數名稱,也可以直接在這個位置匿名函式(通常不建議)。
  • ... 會以as.list()處理後,以list的形式傳入FUN滿足FUN的參數列。

l in lapply for loop? s in sapply for simplify

> x <- list(a=rnorm(10), b=rnorm(100))
#把lapply回傳的list賦值給result
> result <- lapply(x, summary)
> result
$a
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-1.5840 -0.6913 -0.1396  0.1010  1.0960  1.9760 

#中央極限定理,N愈大mean愈接近0
$b
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-3.44800 -0.69560  0.07730  0.02856  0.80970  2.62400 

#result是一個list of list
> class(result)
[1] "list"
> class(result[1])
[1] "list"

#如果是以sapply的方式做處理就不同了
> result <- sapply(x, summary)
> result
              a        b
Min.    -1.5840 -3.44800
1st Qu. -0.6913 -0.69560
Median  -0.1396  0.07730
Mean     0.1010  0.02856
3rd Qu.  1.0960  0.80970
Max.     1.9760  2.62400
#這時sapply回傳的資料因為每個元素都等長,所以判定為matrix
> class(result)
[1] "matrix"

split()

用法:split(x, f, drop = FALSE, ...)

說明:把vector內的元素分類到factor對應位置上定義的group上,各參數的定義:

  • x資料清單
  • f是factor,定義哪個位置的元素要被分到什麼群組(level)。
  • drop定義是否要把沒有分到元素的level去掉。

應用的地方:把值分到各group內,再用其他的looping function 去分析各group所具有的值特性如何,這是R語言分群分析常見的作法,非常重要。

#這個範例需要兩個額外的package,沒有的要先裝
#需要先熟悉dplyr章節或一邊查。
> library(nycflights13)
> library(dplyr) 

#nycflights13裡面有資料表flights,先看看有哪些column。
> names(flights)
 [1] "year"           "month"          "day"            "dep_time"       "sched_dep_time" "dep_delay"     
 [7] "arr_time"       "sched_arr_time" "arr_delay"      "carrier"        "flight"         "tailnum"       
[13] "origin"         "dest"           "air_time"       "distance"       "hour"           "minute"        
[19] "time_hour"

#取出month欄位為9~12月的資料
> x <- filter(flights, month >= 9)

#如果目的是觀察不同month的飛行時間有什麼差別~~
#作法是以month分群,再比較各組之間的飛行時間
#假設要比較的是飛行時間的mean,作法如下:

#以x$air_time作資料,x$month作factor
> x <- split(x$air_time, x$month)

#把NA去掉,看各組平均有什麼不一樣
#btw,可觀察到標示level的維度用``標示起來,需要用x$`9`取值
> lapply(x, mean, na.rm = TRUE)
$`9`
[1] 143.4712

$`10`
[1] 148.8861

$`11`
[1] 155.4686

$`12`
[1] 162.591
#或可用sapply來做
#可看到9-12月飛行時間平均值逐月變高
> sapply(x, mean, na.rm = TRUE)
       9       10       11       12 
143.4712 148.8861 155.4686 162.5914 

#當然也能把data.frame照factor分割成幾個子data.frame存入list...

tapply tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)可視為lapply或sapply加上split。

  • X 等著被分類的vector
  • INDEX Factor
  • FUN 要套用的function
  • ... 給FUN的參數
  • simplify TRUE/FALSE相當於sapply/lapply+split的差別

apply
function (X, MARGIN, FUN, ...)對陣列(Array)中的某個維度套用函式,速度上並不會比寫loop快,但是方便在console中下指令,常用來快速計算Col/Row的總和或平均:

  • X 要處理的陣列(高維度 只能是Array,matrix在高維度無法計算,因為...我試過)。
  • MARGIN 指定某維度處理,就二維陣列來說1代表row;2代表column,三維以上的n維資料需要用大小維n-1的vector指定函式要套用的維度,以一個大小c(5,6,7)的三維陣列來說,MAGRIN帶入c(3,2)代表把第3、2維度的資料平均,每一筆資料的大小是5,所得到的矩陣的大小是c(原陣列第三維大小,原陣列第一維大小),這裡不是很好懂喔,務必每個字用力看完連結起來才能理解。
  • FUN... 要套用的function與他的快樂參數。
#以2維陣列為例
> x <- matrix(rnorm(15), 3, 5)
> x
            [,1]      [,2]       [,3]      [,4]       [,5]
[1,] -0.02375525  1.646059 -0.9159770 1.4699183  0.4711965
[2,] -0.53446637 -1.584217 -0.7435603 0.8063436 -0.3150133
[3,] -0.13293834  1.975515 -0.1463013 1.2771603 -0.3068395
> apply(x, 1, mean)
[1]  0.5294884 -0.4741826  0.5333193
> apply(x, 2, mean)
[1] -0.23038665  0.67911926 -0.60194619  1.18447410 -0.05021877

#以3為陣列為例
> y <- array(rnorm(105), c(3, 5, 7))
> apply(y, c(2,3), mean)
            [,1]       [,2]       [,3]       [,4]       [,5]        [,6]       [,7]
[1,] -1.10116049 -0.5275947  0.0304933 -0.6356397 -0.4271730 -0.28947457  0.9132292
[2,] -0.23953555  0.1877777 -0.1805146 -0.1254264  0.7093323 -0.00134389 -0.4557870
[3,] -0.07977013 -0.2130288  0.2032276  1.0838883  0.1289156  0.07675923  0.8414088
[4,] -0.29096530 -0.2734326  0.3380786  0.3248017  0.2510622 -0.08762972  0.7863718
[5,]  0.12816472 -0.3932561 -0.3754301  0.5006035  0.5095342 -1.27897267 -0.0174905

如果只是要求row/column的sum/mean,有更特化的函式可以直接使用:

colSums (x, na.rm = FALSE, dims = 1) rowSums (x, na.rm = FALSE, dims = 1) colMeans(x, na.rm = FALSE, dims = 1) rowMeans(x, na.rm = FALSE, dims = 1)

> m <- matrix(rnorm(35), 7, 5)
> colSums(m)
[1] -2.0219545  0.3988152 -0.9707659  1.0355328 -2.2227004
> rowSums(m)
[1] -3.1116904  2.5644270 -3.1475705 -0.9775098 -1.3515193  2.3498650 -0.1070747
> colMeans(m)
[1] -0.28885064  0.05697361 -0.13868084  0.14793326 -0.31752862
> rowMeans(m)
[1] -0.62233807  0.51288540 -0.62951411 -0.19550195 -0.27030386  0.46997299 -0.02141494

--

mapply function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE),解釋起來有點複雜,直接看程式碼:

> str(rnorm)
function (n, mean = 0, sd = 1)  
#下面相當於做三次rnorm
#參數n依序填入1~3,mean依序填入3~5,sd依序填入1~3
> mapply(rnorm, 1:3, 3:5, 1:3)
[[1]]
[1] 1.212527

[[2]]
[1] 1.442751 4.947334

[[3]]
[1] 3.121546 8.634739 3.581960
#跟下面效果一樣
> list(rnorm(1,3,1),rnorm(2,4,2),rnorm(3,5,3))
[[1]]
[1] 3.186405

[[2]]
[1] 3.379362 3.193535

[[3]]
[1] 3.885325 3.246838 4.477587

Vectorize Vectorizing a Function,依序把清單上的參數執行一次,而不是直接把清單作參數,但目標參數一定要有名稱而不可是...

> mySum <- 
+     function(arg1, arg2){
+         sum(arg1,arg2)
+     }
#下面這一段:
> x <- 1:10
> y <- 11:20
> v <- vector()
> for(i in 1:length(x)){
+     v[i] <- mySum(x[i],y[i])
+ }
> v
 [1] 12 14 16 18 20 22 24 26 28 30

 #可以用Vectorize取代為這樣:
> vMySum <- Vectorize(mySum, c("arg1","arg2"))
> vMySum(1:10,11:20)
 [1] 12 14 16 18 20 22 24 26 28 30
 #方便吧...但解釋起來一點也不簡單,直接看比較快。

Reference

R-Manual_lappy

results matching ""

    No results matching ""