1. 程式人生 > >R: 矩陣運算及常用函式 III

R: 矩陣運算及常用函式 III

在”矩陣運算及常用函式I“裡已經提到過,apply系列函式“主要用於某維度上某函式/方法的批量應用”,可以避免“控制流迴圈帶來的高錯誤率以及漫長的響應時間”。 為了比較響應時間,我們可以先做一個簡單的測試:為一列資料做一一系列冪的變形,依據指數的sequence,生成一系列的新資料。然後用system.time()來測試執行的時間(如何解讀system.time()結果)。 test1. for迴圈 > power1 <- function(var = EuStockMarkets[,1]){
+   new.var <- NULL
+   for(i in seq(.001, .999, .001)){
+     if(is.null(new.var)){
+       new.var <- var^i
+     }else{
+       new.var <- data.frame(new.var, var^i)
+     }
+  }

+  return(new.var)
+ }
> system.time(power1())    user  system elapsed     1.06    0.00    1.08  這裡elapsed說明呼叫和執行程式到整體執行完一共花費的時間,這段迴圈的function花費了1.08秒 test2. sapply > power2<-function(i)EuStockMarkets[,1]^i > system.time(sapply(seq(.001,.999,.001),power2))    user  system elapsed
   0.42    0.00    0.42 
這個地方, 當我們使用sapply進行處理的時候,總共消耗了0.42秒,比for迴圈快了一倍多,雖然只快了不到一秒,但是執行效率有相當大幅度的提升。當處理的維度變得原來越多的時候(比如for的迴圈巢狀),apply函式的優勢就會更明顯。 -+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+- 在help system中搜索apply > ??apply 可以看到在{base}中自帶的跟apply相關的函式有7個:
base::apply Apply Functions Over Array Margins
base::by Apply a Function to a Data Frame Split by Factors
base::eapply Apply a Function Over Values in an Environment
base::lapply Apply a Function over a List or Vector
base::mapply Apply a Function to Multiple List or Vector Arguments
base::rapply Recursively Apply a Function to a List
base::tapply Apply a Function Over a Ragged Array
其中,lapply下還有不同形式的函式: sapply, vapply, replicate, simplify2array。這樣一來,*apply一族裡一共有11個可用函式。接下來我們打亂順序學習。 -+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+- 1. apply(X, MARGIN, FUN, ...) 對陣列按指定維度以指定函式進行計算。 引數: X: 一個數組,包括矩陣。 MARGIN: 可以是單一的數字,也可以是一個vector指定X的維度,同樣,也可以是包含dimension names的字元向量。在一個2維陣列中,1代表行,2代表列,c(1,2)則是同時應用FUN到行和列上。 FUN: 將要被應用的函式。 ...: 如果FUN需要明確一些引數,則把引數轉移到...處。 > apply(mtcars, 2, mean)[1:5]     'calculate the column mean        mpg        cyl       disp         hp       drat   20.090625   6.187500 230.721875 146.687500   3.596563  當然以上效果也可以直接通過colMeans(mtcars)來實現,這裡只是提供一個使用思路。 當陣列只有2維時MARGIN = c(1,2)對部分函式來說是無效的,如apply(mtcars, c(1,2), mean)得到的還是mtcars這個陣列本身。但是維度達到3以上,MARGIN就會又更明顯的作用。 > z <- array(1:24, dim = 2:4) > zseq <- apply(z, 1:2, max) > zseq      [,1] [,2] [,3] [1,]   19   21   23 [2,]   20   22   24 --------------------------------------------------------------------------------- 2. tapply(X, INDEX, FUN = NULL, ..., simplify =T) 對基本因子進行分組統計 引數: X: 基本物件。如vector或factor。 INDEX: 包含一個或多個factor的list,每個factor的長度都應該同X相當。INDEX用於將X歸類。 simplify: 如果simplify = F, 則所有返回的結果均以list形式呈現,T表示簡化返回的結果。 > fact <- factor(rep(1:4, length = 18)) > tapply(fact, fact, length)    #same as table(fact) 1 2 3 4  5 5 4 4  如果上面這段不足以解釋tapply的作用,我們可以看看下面這段 > tapply(1:18, fact, sum)  1  2  3  4  45 50 36 40  上面這段code的作用就是將fact展開(fact只包含1到4四個level),然後將1:18分別迴圈對應到1到4下面,然後將每列相加。換一種說法,就像是把1:18順序放到列名為1,2,3,4的表格中,然後求colSums: Col.Names 1  2  3  4  Row1      1  2  3  4  Row2      5  6  7  8  Row3      9 10 11 12  Row4     13 14 15 16  Row5     17 18 colSums  45 50 36 40 當INDEX時一個多元的list的時候,情況會複雜一點。 > ind <- list(c(1, 2, 2, 2), c("A", "A", "B", "B")) > table(ind)      ind.2 ind.1 A B     1 1 0     2 1 2 上面那個對ind使用的table(),作用是將list中的兩個對應起來,這樣,A和1對應只有一次,A和2對應一次,B和2對應2次,而1和B沒有對應,所以就有了上表(即維度為ind.1和ind.2的那個結果)。如果我這時呼叫tapply會有什麼作用呢? > tapply(1:4, ind)     #FUN is not specified [1] 1 2 4 4 > tapply(1:4, ind, sum)     #FUN is specified   A  B 1 1 NA 2 2  7 第一段的處理是:在FUN缺失時的情況下,tapply預設返回一系列下標,當FUN明確的時候,X的數值就按照這一系列下標所表示的對應位置做計算。需要注意的是,ind此時是一個2元的list,我們可以將table(ind)的結果看作一個matrix: M, 那麼M[1] 就是A1,M[2]就是A2,M[3]就是B1,M[4]就是B2。 此例中由於M[3]位置為空,而M[4]的位置上有兩個資料,所以tapply(1:4, ind)的後兩個元素返回的下標都是4。當明確了FUN為sum的時候,1:4的後兩個元素在M[4]的位置上相加,就得到7。 回想前一篇講aggregate這個函式,其實也可以實現類似效果,對比一下tapply(1:4, ind, sum)也許更容易理解。 > aggregate(1:4, ind, sum)   Group.1 Group.2 x 1       1       A 1 2       2       A 2 3       2       B 7 ---------------------------------------------------------------------------------
3. by(data, INDICES, FUN, ..., simplify = T) 將tapply發揚光大到matrix和data frame 引數不再贅述,與tapply大同小異。 >  by(mtcars[,c(1,3,4)], mtcars$cyl, sum) mtcars$cyl: 4 [1] 2358.8 ------------------------------------------------------------  mtcars$cyl: 6 [1] 2277.4 ------------------------------------------------------------  mtcars$cyl: 8 [1] 8083.8 > by(mtcars[,1], mtcars[,c(2,9)], sum)
cyl: 4 am: 0 [1] 68.7 ------------------------------------------------------------  cyl: 6 am: 0 [1] 76.5 ------------------------------------------------------------  cyl: 8 am: 0 [1] 180.6 ------------------------------------------------------------  cyl: 4 am: 1 [1] 224.6 ------------------------------------------------------------  cyl: 6 am: 1 [1] 61.7 ------------------------------------------------------------  cyl: 8 am: 1 [1] 30.8 看到這樣的結果,是不是讓人忍俊不禁又想起了aggregate函式。。。 > aggregate(mtcars[,1], mtcars[,c(2,9)], sum)
  cyl am     x 1   4  0  68.7 2   6  0  76.5 3   8  0 180.6 4   4  1 224.6 5   6  1  61.7 6   8  1  30.8 --------------------------------------------------------------------------------- 4. eapply(env, FUN, ..., all.names = F, USE.NAMES = T) 對一個環境中的變數進行計算 > .GlobalEnv$b <- 2:11     #Clear all variables in the global env. before using this clause > .GlobalEnv$a <- 1:10 > eapply(.GlobalEnv, mean) $a [1] 5.5 $b [1] 6.5 > unlist(eapply(.GlobalEnv, mean, USE.NAMES = FALSE)) [1] 5.5 6.5 不多講這個了,目前我還沒用過這個函式,不敢斷言用途是否廣泛。 -+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+- 說到這裡,我們可以暫停一下,lapply,rapply和mapply留待下回分解。 ref: 請自行搜尋以上四個函式的R Documents