在任何一个R语言问答网站或者论坛,你都能看见这样的问题:
Q:如何用循环做【…各种奇怪的事情…】 A:不用用循环哦,亲!apply函数可以解决这个问题哦,亲!
上面答案中提到的这个神奇的apply函数到底是什么?下面通过一些简单的操作示范一下。
打开R,敲入??apply函数,选定base包部分你会看到下面的东西:
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
下面一一示范。
- apply
先看看帮助文档中对其的描述:
“Returns a vector or array or list of values obtained by applying a function to margins of an array or matrix. ”
好吧,vector、array和function是神马我都明白,margins是什么?我来翻译一下吧,margins为1时是指行,margins为2时是指列,如果是c(1:2),好吧,这个啰嗦的举动,指的是整个array或者matrix。
例子如下:
#创建一个10行2列的矩阵
m <- matrix(c(1:10,11:20),nrow=10,ncol=2)
#求m的每一行的均值
apply(m,1,mean)
## [1] 6 7 8 9 10 11 12 13 14 15
#求m的每一列的均值
apply(m,2,mean)
## [1] 5.5 15.5
#将m的每个值除以2
apply(m,1:2,function(x) x/2)
## [,1] [,2]
## [1,] 0.5 5.5
## [2,] 1.0 6.0
## [3,] 1.5 6.5
## [4,] 2.0 7.0
## [5,] 2.5 7.5
## [6,] 3.0 8.0
## [7,] 3.5 8.5
## [8,] 4.0 9.0
## [9,] 4.5 9.5
## [10,] 5.0 10.0
最后一个例子仅仅是为了示范,我们有更简单的方法一秒钟实现。
m/2
## [,1] [,2]
## [1,] 0.5 5.5
## [2,] 1.0 6.0
## [3,] 1.5 6.5
## [4,] 2.0 7.0
## [5,] 2.5 7.5
## [6,] 3.0 8.0
## [7,] 3.5 8.5
## [8,] 4.0 9.0
## [9,] 4.5 9.5
## [10,] 5.0 10.0
- by
帮助文档中的描述:
“Function by is an object-oriented wrapper for tapply applied to data frames. ”
事实上,by的功能绝不是这一句话所能描述的。接着读下去,你会看到
“A data frame is split by row into data frames subsetted by the values of one or more factors, and function FUN is applied to each subset in turn. ”
这里,我们用一个带有定性变量的数据进行示范。这个数据集就是著名的iris。
attach(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
#根据Species分类,求前4个变量的均值
by(iris[,1:4],Species,mean)
## Warning in mean.default(data[x, , drop = FALSE], ...):
## 参数不是数值也不是逻辑值:返回NA
## Warning in mean.default(data[x, , drop = FALSE], ...):
## 参数不是数值也不是逻辑值:返回NA
## Warning in mean.default(data[x, , drop = FALSE], ...):
## 参数不是数值也不是逻辑值:返回NA
## Species: setosa
## [1] NA
## ------------------------------------------------------------
## Species: versicolor
## [1] NA
## ------------------------------------------------------------
## Species: virginica
## [1] NA
其实,就是根据一个定性变量将数据分为若干个子集,再对各子集进行apply操作。
- eapply
帮助文档的描述:
“eapply applies FUN to the named values from an environment and returns the results as a list. The user can request that all named objects are used (normally names that begin with a dot are not). The output is not sorted and no enclosing environments are searched. ”
有理解这句话,重点是理解environment这个东西。environment相当于是R里面的一个小系统,这个系统包含有自己的变量和函数等内容。
用一个简单的例子来示范:
#创建一个新的environment
e=new.env()
#在e中创建两个变量
e$a=1:10
e$b=11:20
#求e中变量的均值
eapply(e,mean)
## $a
## [1] 5.5
##
## $b
## [1] 15.5
一般人儿可能不常用environment这个东西。不过,Bioconductor们是例外。
- lapply
帮助文档的描述:
“lapply returns a list of the same length as X, each element of which is the result of applying FUN to the corresponding element of X. ”
这是apply函数的帮助文档中最简明扼要的一个。直接给示范:
#创建一个list
l=list(a=1:10,b=11:20)
#计算list中每个元素的均值
lapply(l,mean)
## $a
## [1] 5.5
##
## $b
## [1] 15.5
#计算list中每个元素的和
lapply(l,sum)
## $a
## [1] 55
##
## $b
## [1] 155
lapply的文档中让我们进一步参考sapply、vapply和replicate。那我们走去看看。
- sapply
帮助文档的描述:
“sapply is a user-friendly version and wrapper of lapply by default returning a vector, matrix or, if simplify=“array”, an array if appropriate, by applying simplify2array(). sapply(x, f, simplify=FALSE, USE.NAMES=FALSE) is the same as lapply(x,f). ”
上面一堆简单说就是,lapply返回的是一个含有两个元素$a和$b的list,而sapply返回的是一个含有元素[[“a”]]和[[“b”]]的vector,或者列名为a和b的矩阵。
示范如下:
#创建一个list
l=list(a=1:10,b=11:20)
#用sapply求均值
l.mean=sapply(l,mean)
#观察返回结果的类型
class(l.mean)
## [1] "numeric"
l.mean[['a']]
## [1] 5.5
- vapply
帮助文档的描述:
“vapply is similar to sapply, but has a pre-specified type of return value, so it can be safer (and sometimes faster) to use.”
直接示范:
l=list(a=1:10,b=11:20)
#用vapply函数计算五分位数
l.fivenum=vapply(l,fivenum,c(Min.=0,"lst Qu."=0,Median=0,"3rd Qu."=0,Max.=0))
class(l.fivenum)
## [1] "matrix" "array"
#结果
l.fivenum
## a b
## Min. 1.0 11.0
## lst Qu. 3.0 13.0
## Median 5.5 15.5
## 3rd Qu. 8.0 18.0
## Max. 10.0 20.0
从结果可以看到,vapply返回的是一个矩阵。矩阵的列名是list的元素,行名取决于函数的输出结果。
- replicate
帮助文档的描述:
“replicate is a wrapper for the common use of sapply for repeated evaluation of an expression (which will usually involve random number generation). ”
replicate是一个非常强大的函数,它有两个强制参数:replications,即操作的重复次数;function,及要重复的操作。还有一个可选择参数:simplify=T,是否将操作结果转化为vector或者matrix。
示范:
replicate(10,rnorm(10))
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 0.09404565 0.6900446 0.20266413 0.5129547 1.0128168 0.05653415
## [2,] -0.91284275 0.4148158 0.52344728 1.6220620 -0.1530737 0.05569266
## [3,] -2.82534108 -0.8308159 -0.80378790 -0.4079487 -2.1890716 -0.63564857
## [4,] -0.88857743 -0.3077837 -1.04967642 0.3230530 0.1094893 0.38889519
## [5,] 0.21849109 1.2910931 -1.31689751 -0.8643641 -1.6985050 -0.72514369
## [6,] 0.42406931 1.0962243 0.03433455 -0.3644647 -1.1519254 -0.51264405
## [7,] -0.95389493 0.8339559 -0.48655834 0.0271446 0.8224275 -0.75322140
## [8,] 1.30411129 0.6961557 -0.94826540 3.8187493 1.9491568 -0.70021472
## [9,] 1.42619896 1.3484791 -1.77604799 0.2154067 0.3761675 -0.24362034
## [10,] -0.12993140 0.2320959 -0.08696657 1.2382602 0.5781700 -1.26176561
## [,7] [,8] [,9] [,10]
## [1,] 0.29583451 1.7547814 -0.4761433 -2.9786950
## [2,] -1.51743386 -2.1529347 0.3729536 0.2775196
## [3,] -0.46015526 -0.7021302 -0.3539648 -0.6730854
## [4,] 0.89778909 0.1765478 1.9319342 0.3784471
## [5,] 1.42831439 -0.3437932 0.3734149 0.3974802
## [6,] 0.07412502 -2.5053443 -1.0342991 0.8934034
## [7,] 0.04454659 -0.1709211 -0.4627233 1.0121957
## [8,] -0.46424059 -0.2812505 -0.3270812 0.2869938
## [9,] 0.11599570 1.5177862 0.2759767 0.8952857
## [10,] 0.59676528 -0.1538958 -0.9503086 -0.3969758
- mapply
帮助文档的描述:
“mapply is a multivariate version of sapply. mapply applies FUN to the first elements of each … argument, the second elements, the third elements, and so on. Arguments are recycled if necessary. ”
如果你认真看看mapply的帮助文档的话,我打赌你会看到头大。这里给看官两个简单的例子:
l1=list(a=1:10,b=11:20)
l2=list(c=21:30,d=31:40)
l1
## $a
## [1] 1 2 3 4 5 6 7 8 9 10
##
## $b
## [1] 11 12 13 14 15 16 17 18 19 20
l2
## $c
## [1] 21 22 23 24 25 26 27 28 29 30
##
## $d
## [1] 31 32 33 34 35 36 37 38 39 40
#计算l1,l2中各元素的和
mapply(sum,l1$a,l1$b,l2$c,l2$d)
## [1] 64 68 72 76 80 84 88 92 96 100
竖直看下来,加总就得到了上面的结果。
- rapply
帮助文档的描述:
“rapply is a recursive version of lapply. ”
这个描述是史上最差描述之一。因为rapply跟recursive并没有太大关系。rapply的创造性在于提供了一个结果输出形式的参数。
示范:
#创建list
l=list(a=1:10,b=11:20)
#计算l中元素的log2
rapply(l,log2)
## a1 a2 a3 a4 a5 a6 a7 a8
## 0.000000 1.000000 1.584963 2.000000 2.321928 2.584963 2.807355 3.000000
## a9 a10 b1 b2 b3 b4 b5 b6
## 3.169925 3.321928 3.459432 3.584963 3.700440 3.807355 3.906891 4.000000
## b7 b8 b9 b10
## 4.087463 4.169925 4.247928 4.321928
#将结果的输出形式设定为list
rapply(l,log2,how="list")
## $a
## [1] 0.000000 1.000000 1.584963 2.000000 2.321928 2.584963 2.807355 3.000000
## [9] 3.169925 3.321928
##
## $b
## [1] 3.459432 3.584963 3.700440 3.807355 3.906891 4.000000 4.087463 4.169925
## [9] 4.247928 4.321928
#计算均值
rapply(l,mean)
## a b
## 5.5 15.5
rapply(l,mean,how="list")
## $a
## [1] 5.5
##
## $b
## [1] 15.5
综上,rapply函数的输出结果取决于函数和how参数。当how=“list"时,数据的原始结构被保留,否则,输出结果被转化为vector。
当然,看官还可以将classes函数传递给rapply函数。例如在混合型list中,可以通过classes=numeric,是的函数子对数字型元素进行操作。
- tapply
帮助文档的描述:
“Apply a function to each cell of a ragged array, that is to each (non-empty) group of values given by a unique combination of the levels of certain factors.”
语焉不详。翻译一下大概是:
“tapply(X, INDEX, FUN = NULL, …, simplify = TRUE)”中的“X”是 “an atomic object, typically a vector.”,而“INDEX”是“list of factors, each of same length as X. The elements are coerced to factors by as.factor.”
依然以iris数据集为例示范一下:
attach(iris)
## The following objects are masked from iris (pos = 3):
##
## Petal.Length, Petal.Width, Sepal.Length, Sepal.Width, Species
#根据sprcies进行分类,计算petal的均值
tapply(iris$Petal.Length,Species,mean)
## setosa versicolor virginica
## 1.462 4.260 5.552
简短的总结:
这里给出的都是极其简单的例子,基于最简单的数据,和最简单的函数。因为对于每一个操作而言,看官都可查看数据的操作前状态和操作后状态,这样便于看官知道,操作到底对数据干了什么事情。
当然了,apply函数的功能不限于文中介绍的这些,进一步的功用期待看官自己去挖掘。
给出几个使用apply函数的建议,在使用之前应当思考:
- 原始数据是什么类型?vector?matrix?data frame?….
- 想对原始数据的哪些子集进行操作?行?列?所有元素?….
- 操作将返回什么结果?原始数据的结构是如何变化的?
只是一个老生常谈的关于“输入——操作——输出”的故事:你有什么?你想要什么?两者之间需要什么?