作者:
鲁伟
,热爱数据,坚信数据技术和代码改变世界。R语言和Python的忠实拥趸,为成为一名未来的数据科学家而奋斗终生。个人公众号:数据科学家养成记 (微信ID:louwill12)
当初入坑R语言的时候,就在各种场合看到老司机的忠告,“尽量避免使用循环!”一开始并不明白这其中的奥义,直到后来对R语言有深入接触后,才领会R语言在向量化运算方面的强大功能。本篇内容就总结小编在使用R语言向量化运算apply函数族的一些心得体会。
至于R写循环为什么执行效率低下,小编也从技术论坛上得到了一些解释。说是像for和while之类的循环语句是基于R本身来实现的,而apply函数族的向量化运算是基于C语言函数来实现的,所以二者的计算效率有很大差距。小编不是计算机出身,也不懂C,既然大神们这么说了就知道是这么回事就好啦。
所谓apply函数族,指的是以apply函数为核心,包括lapply, sapply, tapply, mapply, rapply, vapply, eapply等在内八个函数。先上个表格具体理解下每个函数的含义:
函数名
|
含义
|
apply
|
apply
|
lapply
|
list apply
|
sapply
|
simplify list apply
|
tapply
|
table apply
|
mapply
|
mutiple list apply
|
rapply
|
recursively apply
|
vapply
|
vector apply
|
eapply
|
environment apply
|
再上个图看一下apply函数族各函数之间的关系:
(图片来自张丹老师博客http://blog.fens.me/r-apply/)
作为该函数族最原始最核心的函数,apply函数一直担任着向量化运算的艰巨任务。apply函数可以对矩阵和数组以及数据框按照指定的函数在不同维度上进行循环运算,其用法如下:
apply(X,margin,FUN,...)
X:矩阵,数组,数据框
margin:对象维度,表示按行还是按列,1表示行2表示列
FUN:对X执行运算的函数
具体用法可看实例:
x dimnames(x)[[1]] (x1x1 x2
3 3
(col.sums x1 x2
24 24
(row.sums a b c d e f g h
7 6 5 4 5 6 7 8
rbind(cbind(x, Rtot = row.sums),
Ctot = c(col.sums, sum(col.sums)))
x1 x2 Rtot
a 3 4 7
b 3 3 6
c 3 2 5
d 3 1 4
e 3 2 5
f 3 3 6
g 3 4 7
h 3 5 8
Ctot 24 24 48
tapply函数应用于分组循环计算,其功能类似于dplyr包中的group by函数。其用法如下:
tapply(X,INDEX,FUN,...,simplify=TRUE)
X:向量
INDEX:用于分组的索引
FUN:应用于X的函数
. . .:接收多个数据
simplify:返回值是否数组化
用法实例:对iris数据集中按花种类计算花瓣宽度的均值
attach(iris)
tapply(Petal.Width,Species,mean)
setosa versicolor virginica
0.246 1.326 2.026
这两个函数放一起讲,那当然是它们实在太相似了。lapply函数顾名思义,list apply,除了其对象参数是一个list或者data.frame之外,其返回值也是一个list。而sapply函数作为一个简化版的lapply,其返回值形式可以不是list。二者用法如下:
lapply(X,FUN,...)
sapply(X,FUN,...,simplify=TRUE,USE.NAMES=TRUE)
X:矩阵,数组,数据框
FUN:作用在对象上的函数
. . .:其他参数(可选)
simplify:是否数组化
USE.NAMES:若X为一个字符串,TRUE表示设置字符串为数据名
用法实例:
elapply(e, fivenum)
$alpha
[1] 3 4 5 6 7
$beta
[1] 7.389056 37.341843 148.413159 750.030976 2980.957987
$gamma
[1] 0.0 0.0 0.5 1.0 1.0
#simplify参数为FALSE时,sapply与lapply效果一样
sapply(e, fivenum,simplify=FALSE)
$alpha
[1] 3 4 5 6 7
$beta
[1] 7.389056 37.341843 148.413159 750.030976 2980.957987
$gamma
[1] 0.0 0.0 0.5 1.0 1.0
#simplify参数为TRUE时,sapply函数返回值数组化
sapply(e, fivenum,simplify=TRUE)
alpha beta gamma
[1,] 3 7.389056 0.0
[2,] 4 37.341843 0.0
[3,] 5 148.413159 0.5
[4,] 6 750.030976 1.0
[5,] 7 2980.957987 1.0
vapply函数与sapply函数也较为类似,不同的是它可以通过FUN.VALUE参数对返回值行名进行预定义。其用法如下:
vapply(X,FUN,FUN.VALUE,...,USE.NAMES=TRUE)
FUN.VALUE:通用型向量,以控制返回值的行名
用法实例:
g vapply(g,cumsum,FUN.VALUE=c('a'=0,'b'=0,'c'=0,'d'=0))
x1 x2
a 3 2
b 6 3
c 9 7
d 12 12
rapply函数可以理解为一个递归版本的lapply,同样只处理list类型的数据,对list中的每个元素进行递归遍历,如果list元素还包括子元素也需要继续遍历。其用法如下:
rapply(X, FUN,classes = ANY,deflt=NULL,how=c(unlist, replace, list),...)
X:list对象
FUN:作用在对象上的函数
classes:关于类名的字符向量,若为any则表示为任意类型
deflt:默认结果,若how参数选择了replace则不能使用
how:字符串匹配的三种结果
. . .:更多参数
rapply用法实例:
X rapply(X, sqrt, classes = "numeric", how = "replace")
[[1]]
[[1]]$a
[1] 1.772454
[[1]]$b
[[1]]$b$c
[1] 1
$d
[1] "a test"
mapply函数也是sapply函数的近亲,从函数名来看就知道mapply函数可以接受多个数据对象。其使用格式较前面几个有略微区别:
mapply(FUN,...,MoreArgs = NULL,SIMPLIFY = TRUE,USE.NAMES=TRUE)
FUN:作用对象函数