R 語言有很多強大的循環函數,與其他程式語言(比如 Java、C/C++、Python、Julia 等)手寫循環不同,R 使用內置的循環函數會讓你的代碼更具有可讀性、更簡潔。下面介紹 7 種 apply 族循環函數和 2 個其他循環函數,讓你在數據統計和建模時更有效率。
一、apply函數
函數用法:
apply(X, MARGIN, FUN, ...)
函數前面接受 3 個參數,返回一個向量。其中第一個參數 X 是向量或者矩陣,第三個 FUN 是函數,這個apply函數就是把 X 傳遞給 FUN 函數進行循環計算。第二個參數 MARGIN 如果等於 1,那麼就按行進行計算,如果是 2,那麼就是按照列計算。
比如求一個矩陣的所有行的和,運行下面代碼要先安裝 matlab 包,第一行進行包加載,第 17 行用包中的 magic 函數生成一個魔方矩陣:
> library(matlab)
載入程輯包:『matlab』
The following object is masked from 『package:stats』:
reshape
The following objects are masked from 『package:utils』:
find, fix
The following object is masked from 『package:base』:
sum
> (m <- magic(5)) [,1] [,2] [,3] [,4] [,5][1,] 17 24 1 8 15[2,] 23 5 7 14 16[3,] 4 6 13 20 22[4,] 10 12 19 21 3[5,] 11 18 25 2 9我們可以用 R 的 rowSums 加總每一行:
> rowSums(m)[1] 65 65 65 65 65每一行之和是 65,下面用 apply 函數進行計算,第二個參數為 1:
> apply(m, 1, sum)[1] 65 65 65 65 65計算的結果跟上面一樣,當然這個例子體現不出 apply 的優勢。假如要對矩陣的每一行都轉化為字符串,那麼就可以用 apply 函數進行計算而不用手寫循環了:
> apply(m, 1, toString)[1] "17, 24, 1, 8, 15" "23, 5, 7, 14, 16" "4, 6, 13, 20, 22" "10, 12, 19, 21, 3" "11, 18, 25, 2, 9"以上是 apply 函數的基本用法,第一個參數 X 也可以是數據框,第三個參數可以是你自己定義的函數。
ps:魔方矩陣的列加總:
> apply(m, 2, sum)[1] 65 65 65 65 65列的和跟行的和是一樣的。
二、lapply函數
函數用法:
函數前面接受兩個參數,並返回一個列表。X 可以是一個向量或者矩陣等,FUN 是一個函數,作用於 X 的每一個元素上。
比如有下面一個列表:
> num <- list(a = c(2, 3, 3, 4), b = c(3, 4, 4, 5), c = 2, d = c(1, 2, 3), e = 2)> num$a[1] 2 3 3 4
$b[1] 3 4 4 5
$c[1] 2
$d[1] 1 2 3
$e[1] 2如果想要去除列表中,每個元素的重複值(只保留一個,比如 2, 2, 2,只保留一個2),用手寫循環的方式比較麻煩的,這時就可以用到 lapply 函數:
> lapply(num, unique)$a[1] 2 3 4
$b[1] 3 4 5
$c[1] 2
$d[1] 1 2 3
$e[1] 2用 unique 函數作用於列表中的每一個元素上,得到列表中每個元素中的值都是唯一的,達到去重的目的。
當然,這只是 lapply 函數一個很簡單的用法,實際開發時,你可以定義自己的 FUN 函數,可以開發出很複雜的算法。不過,總的來說 lapply 函數對用戶還是比較友好的。
三、vapply 函數
函數用法:
vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)該函數跟前面的 lapply 函數一樣,FUN 作用於 X 中每一個元素。它最大的特點就是返回一個簡化版的列表或向量,並且,它需要第三個參數 FUN.VALUE 來指定返回值的模板。
比如上面例子的列表中計算每一個元素的長度:
> num$a[1] 2 3 3 4
$b[1] 3 4 4 5
$c[1] 2
$d[1] 1 2 3
$e[1] 2
> vapply(num, length, numeric(1))a b c d e 4 4 1 3 1或者把前面的魔方矩陣 m 的每一個元素進行開方,然後以簡化的向量或數組輸出:
> m[,1] [,2] [,3] [,4] [,5][1,] 17 24 1 8 15[2,] 23 5 7 14 16[3,] 4 6 13 20 22[4,] 10 12 19 21 3[5,] 11 18 25 2 9> vapply(m, sqrt, numeric(1))[1] 4.123106 4.795832 2.000000 3.162278 3.316625 4.898979 2.236068 2.449490 3.464102 4.242641 1.000000 2.645751 3.605551[14] 4.358899 5.000000 2.828427 3.741657 4.472136 4.582576 1.414214 3.872983 4.000000 4.690416 1.732051 3.000000可以看出,vapply 函數沒有前面那兩個函數那麼靈活,但是它在某些情況下還是比較有用的,比如我想操作某個數據結構中的每個元素,但又不想在保留原有數據結構,而只是想把操作有的數據進行原始的列表或向量輸出,那麼 vapply 函數就可以派上用場了。
四、sapply 函數
函數用法:
sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)sapply 函數是介於 lapply 函數和 vapply 函數之間的一個簡化(simplfy),就是如果你既想作用於每一個函數,又不想硬性指定返回值的模板,又想要一個簡化的結果,那麼 sapply 函數就可以滿足你的要求了。現在可以用前面的例子換 sapply 函數計算一下:
> sapply(num, length)a b c d e4 4 1 3 1 > sapply(m, sqrt)[1] 4.123106 4.795832 2.000000 3.162278 3.316625 4.898979 2.236068 2.449490 3.464102 4.242641 1.000000 2.645751 3.605551[14] 4.358899 5.000000 2.828427 3.741657 4.472136 4.582576 1.414214 3.872983 4.000000 4.690416 1.732051 3.000000由此可見,sapply 函數還是很方便的,它不用過多指定參數,但會儘可能的簡化結果並輸出來。這函數使用頻率還是比其他同族函數要高的。
下篇預告:mapply、tapply、rapply