本文介绍R语言中的apply函数簇,apply函数簇是使用R做科学计算的基本函数簇之一,下面的例子参考了刘顺祥每天进步一点点R语言教程,编译成notebook形式,便于学习!
如果计算涉及到一个单一的向量,而结果也是一个向量,tapply函数是一个可选项,不同于 aggregate函数,它返回一个向量或数组,这使得其单个元素很容易被访问。
将组定义为矩阵的行或列,即操作目标为矩阵的每一列或行时,apply函数时最佳选择。该 函数通常会返回一个向量或数组,但根据行或列操作的结果维度不同,将返回一个列表。
将组定义为列表中的元素。如果组已经形成列表元素,那么sapply或lapply函数比较适合, 它们的区别是lapply返回一个列表,而sapply可将输出简化为向量或数组。有时可以结合使 用split函数,将需要处理的数据创建为一个列表,然后再使用这两个函数。
如果所要计算函数的参数为一个矩阵或数组,可以考虑使用mapply函数,该函数非常的灵 活和简单,其返回的结果一般是列表形式。
#如果想分析iris数据集中Sepal.Length在各个花种中的最大值,可以通过tapply函数实现,这里的Sepal.Length和Species为两个向量,且各自的长度均相等。
max_sepal.length<-tapply(iris$Sepal.Length,iris$Species,max)
max_sepal.length
## setosa versicolor virginica
## 5.8 7.0 7.9
#rpois make a set of poisson distribution;rt make a set of student distribution.
x<-rpois(100,2)
y<-rpois(100,3)
z<-rt(100,2,3)
data<-data.frame(x=x,y=y,z=z)
head(data)
## x y z
## 1 2 1 2.3771857
## 2 4 3 5.3422753
## 3 2 4 0.8927298
## 4 1 1 2.0148833
## 5 1 4 2.5353424
## 6 5 5 4.8125707
tapply(data$z,INDEX = list(data$x,data$y),FUN = sum)
## 0 1 2 3 4 5 6
## 0 3.810185 6.750107 12.369562 5.438668 20.392904 12.654777 2.849390
## 1 40.626444 25.575616 26.953215 24.930124 28.103226 6.628725 1.749775
## 2 NA 20.121185 31.358013 43.673581 29.037726 13.647852 3.189423
## 3 2.052972 22.001152 12.895461 13.174466 13.104459 NA NA
## 4 NA 19.229854 1.788801 5.342275 6.309771 NA 7.815427
## 5 NA 5.266137 8.481525 NA 5.083856 4.812571 NA
## 7 9
## 0 NA 7.379446
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 3.067641 NA
## 5 NA NA
#当数据具备数组的特性,可通过apply函数对数据的每个维度进行运算,该函数需要三个参数:需要计算的数组、运算维度的索引号和使用的函数。
mat<-matrix(1:12, ncol = 3)
mat
## [,1] [,2] [,3]
## [1,] 1 5 9
## [2,] 2 6 10
## [3,] 3 7 11
## [4,] 4 8 12
apply(mat,MARGIN = 2,FUN = scale)
## [,1] [,2] [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,] 0.3872983 0.3872983 0.3872983
## [4,] 1.1618950 1.1618950 1.1618950
scale(mat)
## [,1] [,2] [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,] 0.3872983 0.3872983 0.3872983
## [4,] 1.1618950 1.1618950 1.1618950
## attr(,"scaled:center")
## [1] 2.5 6.5 10.5
## attr(,"scaled:scale")
## [1] 1.290994 1.290994 1.290994
#统计各个列的均值,为比较显式循环和apply的隐式循环,程序如下,就可以比较出两种方式的效率:
mat<-matrix(1:100000,ncol = 100)
system.time(apply(mat,2,mean))
## user system elapsed
## 0.000 0.000 0.002
mean_self<-function(data){
row_n<-dim(data)[1]
col_n<-dim(data)[2]
c_m<-numeric(col_n)
for(i in 1:col_n){
sum<-0
for(j in 1:row_n){
sum<-sum+data[j,i]
}
c_m[i]<sum/row_n
}
return(c_m)
}
system.time(mean_self(mat))
## user system elapsed
## 0.052 0.008 0.058
#lapply()函数和sapply()函数把一个列表或向量作为其第一个参数,再把需要应用到每个列表元素的函数作为它的第二个参数。其实它也应用到了循环,是一种隐式的循环,对列表的每一个元素做同样的函数计算。
x<-c('Today is Sunday','The Weather is very nice','Please check your R language synax','R语言 是统计学中 非常实用的 工具')
#strsplit函数是根据某个分隔符将字符串分割为多个单词
split_x<-strsplit(x,' ')
mem_length1<-lapply(split_x,length)
mem_length2<-sapply(split_x,length)
unlist(mem_length1)
## [1] 3 5 6 4
unlist(mem_length2)
## [1] 3 5 6 4
#使用sapply函数的另一个重要问题涉及到数据框。当数据框被视为列表时,数据框的每一列看着独立的列表元素。
sapply(iris,class)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## "numeric" "numeric" "numeric" "numeric" "factor"
sapply(ChickWeight,mode)
## weight Time Chick Diet
## "numeric" "numeric" "numeric" "numeric"
#通过以上的应用,可以提取满足特定条件的数据框的列
r1<-iris[,sapply(iris,class)=='numeric']
r2<-CO2[,sapply(CO2,class)=='factor']
head(r1)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 5.1 3.5 1.4 0.2
## 2 4.9 3.0 1.4 0.2
## 3 4.7 3.2 1.3 0.2
## 4 4.6 3.1 1.5 0.2
## 5 5.0 3.6 1.4 0.2
## 6 5.4 3.9 1.7 0.4
head(r2)
## Type Treatment
## 1 Quebec nonchilled
## 2 Quebec nonchilled
## 3 Quebec nonchilled
## 4 Quebec nonchilled
## 5 Quebec nonchilled
## 6 Quebec nonchilled
#接下来使用自编函数加入到sapply函数中,实现循环。该自编函数的目的是计算出1000个100*5的矩阵中最大相关系数的均值。这里很关键的一点是给自编函数传一个虚拟参数i用来循环。
fun2<-function(i,n,m){
mat=matrix(rnorm(n*m),ncol=m)
corr=cor(mat)
diag(corr)=NA
max(corr,na.rm = TRUE)
}
max_r<-sapply(1:1000,FUN = fun2,n=100,m=5)
mean_r<-mean(max_r)
mean_r
## [1] 0.154922
#mapply函数的应用:该函数的第一个参数为指定的函数,第二个参数为指定函数的参数。如果根据某种正则表达式将一个字符向量的对应特征取出来,例如取出'qaws1few4g'中的'1f'和'4g'
#生成一组字符串向量
char<-c('qwd121dad12d2edas','12123sd12asd12da2d12','qwe1ad123da','zdad1b')
#采用gregexpr函数,使用的正则表达式为'[0-9][a-z]'
parts<-gregexpr(pattern = '[0-9][a-z]',char)
parts
## [[1]]
## [1] 6 11 13
## attr(,"match.length")
## [1] 2 2 2
## attr(,"useBytes")
## [1] TRUE
##
## [[2]]
## [1] 5 9 14 17
## attr(,"match.length")
## [1] 2 2 2 2
## attr(,"useBytes")
## [1] TRUE
##
## [[3]]
## [1] 4 9
## attr(,"match.length")
## [1] 2 2
## attr(,"useBytes")
## [1] TRUE
##
## [[4]]
## [1] 5
## attr(,"match.length")
## [1] 2
## attr(,"useBytes")
## [1] TRUE
#自编函数,取出相应的字符串
fun3<-function(text,len) substring(text,len,len+attr(len,'match.length')-1)
#取出相应的字符串存放到res中
res<-mapply(fun3,char,parts)
res
## $qwd121dad12d2edas
## [1] "1d" "2d" "2e"
##
## $`12123sd12asd12da2d12`
## [1] "3s" "2a" "2d" "2d"
##
## $qwe1ad123da
## [1] "1a" "3d"
##
## $zdad1b
## [1] "1b"
最后总结一下: tapply()的被分析对象必须且只能是向量 apply()的被分析对象必须且只能是矩阵或数组 sapply()的被分析对象必须且只能是向量或列表 lapply()的被分析对象必须且只能是向量或列表 mapply()的被分析对象必须是函数