这个教程目的在于介绍apply()家族在R语言的用法,apply()函数算是R语言里面很基础的一个函数,同时还有sapply()、lapply()、tapply()函数精简了apply()的用法。
apply()函数是一个很R语言的函数,可以起到很好的替代冗余的for循环的作用,在一篇博客里面介绍过,R语言的循环操作for和while,都是基于R语言本身来实现的,而向量操作是基于底层的C语言函数实现的,所以使用apply()家族进行向量计算是高性价比的。apply()可以面向数据框、列表、向量等,同时任何函数都可以传递给apply()函数。
apply()函数
apply()函数的用法如下:
apply(X, MARGIN, FUN)
Here:
-x: 一个数组或者矩阵
-MARGIN: 两种数值1或者2决定对哪一个维度进行函数计算
-MARGIN=1`: 操作基于行
-MARGIN=2`: 操作基于列
-MARGIN=c(1,2)`: 对行和列都进行操作
-FUN: 使用哪种操作,内置的函数有mean(平均值)、medium(中位数)、sum(求和)、min(最小值)、max(最大值),当然还包括广大的用户自定义函数
一个最简单的例子就是使用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()函数
lapply()函数中多出来的l代表的是list,所以lapply()和apply()的区别在于输出的格式,lapply()的输出是一个列表(list),所以lapply()函数不需要MARGIN参数:
lapply(X, FUN)
Arguments:
-X: 一个向量或者是一个对象
-FUN: 对X里面每个元素进行操作的函数
一个很简单的示例操作就是把一个字符向量里面的字符转成小写:
> 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"
我们可以看到,输出的内容是以list形式给出的,为了方便,我们可以使用unlist()
函数进行整合:
> movies_lower <-unlist(lapply(movies,tolower))
> str(movies_lower)
chr [1:4] "spyderman" "batman" "vertigo" "chinatown"
sapply()函数
sapply()函数做的事情和lapply()一样,可以理解为是一个简化的lapply,返回的是一个向量(vector)使得对解读更加友好,其使用方法和lapply一样,不过多了两个参数: simplify
&use.NAMEs
,simplify = T
可以将输出结果数组化,如果设置为false,sapply()函数就和lapply()函数没有差别了,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"
那我们就稍微总结一下这三个函数的区别:
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()函数
这里拓展一个函数:tapply(),它可以对一个向量里面进行分组统计操作。
是不是想起了dplyr包里面的group_by函数 + summarize()函数。
其参数为:
tapply(X, INDEX, FUN = NULL)
Arguments:
-X: 一个对象,一般都是向量
-INDEX: 一个包含分类因子的列表(list)
-FUN: 对X里面每个元素进行操作的函数
数据分析的一部分工作就是分组进行统计,举例来说,根据一个特性来对一个群体进行分组计算平均值。拿鸢尾花数据(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包可以实现同样的目的:
> 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()家族还有多个衍生函数,包括vapply、mapply、rapply等,但是具体应用其实并不很常用,摒弃了for循环和while循环,我们还是有很多方法可以高效而简洁得实现我们的目的得。