R: 矩陣運算及常用函式 III
阿新 • • 發佈:2019-01-31
在”矩陣運算及常用函式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個:
其中,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
+ 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
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 |
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