這個教程目的在于介紹apply()家族在R語言的用法笤妙,apply()函數(shù)算是R語言里面很基礎(chǔ)的一個函數(shù),同時還有sapply()淹父、lapply()株婴、tapply()函數(shù)精簡了apply()的用法。
apply()函數(shù)是一個很R語言的函數(shù)暑认,可以起到很好的替代冗余的for循環(huán)的作用困介,在一篇博客里面介紹過,R語言的循環(huán)操作for和while蘸际,都是基于R語言本身來實現(xiàn)的座哩,而向量操作是基于底層的C語言函數(shù)實現(xiàn)的,所以使用apply()家族進行向量計算是高性價比的粮彤。apply()可以面向數(shù)據(jù)框根穷、列表、向量等导坟,同時任何函數(shù)都可以傳遞給apply()函數(shù)屿良。
apply()函數(shù)
apply()函數(shù)的用法如下:
apply(X, MARGIN, FUN)
Here:
-x: 一個數(shù)組或者矩陣
-MARGIN: 兩種數(shù)值1或者2決定對哪一個維度進行函數(shù)計算
-MARGIN=1`: 操作基于行
-MARGIN=2`: 操作基于列
-MARGIN=c(1,2)`: 對行和列都進行操作
-FUN: 使用哪種操作,內(nèi)置的函數(shù)有mean(平均值)惫周、medium(中位數(shù))尘惧、sum(求和)、min(最小值)递递、max(最大值)喷橙,當然還包括廣大的用戶自定義函數(shù)
一個最簡單的例子就是使用apply()對一個matrix求和,以下代碼是對列求和:
> m1 <- matrix(C<-(1:10),nrow=5, ncol=6)
> m1
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 6 1 6 1 6
[2,] 2 7 2 7 2 7
[3,] 3 8 3 8 3 8
[4,] 4 9 4 9 4 9
[5,] 5 10 5 10 5 10
> a_m1 <- apply(m1, 2, sum)
> a_m1
[1] 15 40 15 40 15 40
lapply()函數(shù)
lapply()函數(shù)中多出來的l代表的是list登舞,所以lapply()和apply()的區(qū)別在于輸出的格式重慢,lapply()的輸出是一個列表(list),所以lapply()函數(shù)不需要MARGIN參數(shù):
lapply(X, FUN)
Arguments:
-X: 一個向量或者是一個對象
-FUN: 對X里面每個元素進行操作的函數(shù)
一個很簡單的示例操作就是把一個字符向量里面的字符轉(zhuǎn)成小寫:
> movies <- c("SPYDERMAN","BATMAN","VERTIGO","CHINATOWN")
> class(movies)
[1] "character"
> movies_lower <-lapply(movies, tolower)
> str(movies_lower)
List of 4
$ : chr "spyderman"
$ : chr "batman"
$ : chr "vertigo"
$ : chr "chinatown"
我們可以看到逊躁,輸出的內(nèi)容是以list形式給出的似踱,為了方便,我們可以使用unlist()
函數(shù)進行整合:
> movies_lower <-unlist(lapply(movies,tolower))
> str(movies_lower)
chr [1:4] "spyderman" "batman" "vertigo" "chinatown"
sapply()函數(shù)
sapply()函數(shù)做的事情和lapply()一樣,可以理解為是一個簡化的lapply核芽,返回的是一個向量(vector)使得對解讀更加友好囚戚,其使用方法和lapply一樣,不過多了兩個參數(shù): simplify
&use.NAMEs
,simplify = T
可以將輸出結(jié)果數(shù)組化轧简,如果設置為false驰坊,sapply()函數(shù)就和lapply()函數(shù)沒有差別了,use.NAMEs = T
可以設置字符串為字符名哮独。
> dt <- cars
> lmn_cars <- lapply(dt, min)
> smn_cars <- sapply(dt, min)
> smn_cars_sim <- sapply(dt, min, simplify = F)
> lmn_cars
$`speed`
[1] 4
$dist
[1] 2
> smn_cars
speed dist
4 2
> smn_cars_sim
$`speed`
[1] 4
$dist
[1] 2
> class(lmn_cars)
[1] "list"
> class(smn_cars)
[1] "numeric"
> class(smn_cars_sim)
[1] "list"
那我們就稍微總結(jié)一下這三個函數(shù)的區(qū)別:
Function | Second Header | Objective | Input | Output |
---|---|---|---|---|
apply | apply(x, MARGIN, FUN) | Apply a function to the rows or columns or both | Data frame or matrix | vector, list, array |
lapply | lapply(X, FUN) | Apply a function to all the elements of the input | List, vector or data frame | list |
sapply | sappy(X FUN) | Apply a function to all the elements of the input | List, vector or data frame | vector or matrix |
tapply()函數(shù)
這里拓展一個函數(shù):tapply()拳芙,它可以對一個向量里面進行分組統(tǒng)計操作。
是不是想起了dplyr包里面的group_by函數(shù) + summarize()函數(shù)皮璧。
其參數(shù)為:
tapply(X, INDEX, FUN = NULL)
Arguments:
-X: 一個對象舟扎,一般都是向量
-INDEX: 一個包含分類因子的列表(list)
-FUN: 對X里面每個元素進行操作的函數(shù)
數(shù)據(jù)分析的一部分工作就是分組進行統(tǒng)計,舉例來說悴务,根據(jù)一個特性來對一個群體進行分組計算平均值睹限。拿鳶尾花數(shù)據(jù)(iris)舉例,其有三個品種:Setosa, Versicolor, Virginica讯檐,以下代碼可以計算三個品種平均寬度:
> data(iris)
> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
> tapply(iris$Sepal.Width, iris$Species, median)
setosa versicolor virginica
3.4 2.8 3.0
使用我之前說的dplyr包可以實現(xiàn)同樣的目的:
> iris %>% group_by(Species) %>% + summarise(mean(Sepal.Width)) # A tibble: 3 x 2 Species `mean(Sepal.Width)` <fct> <dbl> 1 setosa 3.43 2 versicolor 2.77 3 virginica 2.97
Conclusion
其實apply()家族還有多個衍生函數(shù)羡疗,包括vapply、mapply别洪、rapply等叨恨,但是具體應用其實并不很常用,摒棄了for循環(huán)和while循環(huán)挖垛,我們還是有很多方法可以高效而簡潔得實現(xiàn)我們的目的得特碳。