apply()对层、行、列、行和列应用函数,根据观测、变量和数据集不同层次的特征决定。语法格式为:
apply(dataset, MARGIN, FUN)
dataset是apply应用的数据集,数据结构是数组、矩阵或数据框。参数MARGIN是apply()应用的维度,MARGIN=1表示矩阵和数组的行,MARGIN=2表示矩阵和数组的列。参数FUN为应用的计算函数f(),必须大写,可带有f()的参数。FUN函数结果的长度确定apply()的返回值类型,通常为array类型,若返回值的向量长度不等,则返回list对象。
1.apply()的基本应用方法
eg1.指定维数行或列进行计算
> dar1=array(rep(1:3,3),dim=c(3,3)) #按列生成二维数组
> dar1
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 2 2 2
[3,] 3 3 3
> apply(dar1,MARGIN=1,FUN=sd) #对行计算方差
[1] 0 0 0
> apply(dar1,2,mean) #对列计算均值
[1] 2 2 2
eg2.高维计算margin=c(1,2)或3
margin=c(1,2)是对不同层相同行列下标元素的多维计算,margin=3是对层进行的高维计算。
>dar2=array(rep(1:3,each=9),dim=c(3,3,3)) #按列生成三维数组dar2
>dar2
## , , 1 #层
## [,1] [,2] [,3]
## [1,] 1 1 1
## [2,] 1 1 1
## [3,] 1 1 1
## , , 2 #层
## [,1] [,2] [,3]
## [1,] 2 2 2
## [2,] 2 2 2
## [3,] 2 2 2
## , , 3 #层
## [,1] [,2] [,3]
## [1,] 3 3 3
## [2,] 3 3 3
## [3,] 3 3 3
> apply(dar2,3,sum) #margin=3对层应用sum()
[1] 9 18 27
> apply(dar2,1:2,sum) #对不同层行列下标相同的元素计算和
#例如,c(1,1,1)+c(1,1,2)+c(1,1,3)
[,1] [,2] [,3]
[1,] 6 6 6
[2,] 6 6 6
[3,] 6 6 6
若MARGIN>=data的维数,函数f()计算结果将是某一值,甚至产生无效值。
> apply(dar1,1:2,FUN=sd) #二维数组dar1不能进行不同层对应元素的计算
[,1] [,2] [,3]
[1,] NA NA NA
[2,] NA NA NA
[3,] NA NA NA
以上sd()、sum()或mean()对变量应用的返回值长度n=1,与dim(data)[MARGIN]相同。若f()函数返回值长度n>1,则apply函数结果的维度为c(n, dim(data)[MARGIN])。
2.apply()的分组数据应用
函数apply()的应用有计算分组数据的分位数。分位数将数据根据数值范围分成等分量,可知向量整体数值和元素的数值排列。例如,
> dv1=c(1:5)
> dvquan1=quantile(dv1)
> dvquan1
0% 25% 50% 75% 100%
1 2 3 4 5
> dv1.1=c(1:3)
> dvquan1.1=quantile(dv1.1) #数值范围四等分
> dvquan1.1
0% 25% 50% 75% 100%
1.0 1.5 2.0 2.5 3.0
参数prev设置向量等分的范围,例如prev可等于seq(1:0,0.2),即使prev=c(1.0,0.75,0.5,0.2)仍表示四等分。
> dvquan1.2=quantile(dv1,prev=c(1,0.75,0.5,0.25)) #prev的缺省设置
> dvquan1.2
0% 25% 50% 75% 100%
1 2 3 4 5
> dvquan2=quantile(dv1,prev=c(0.75,0.5,0.2)) #prev是等分的参数
> dvquan2
0% 25% 50% 75% 100%
1 2 3 4 5
> dvquan3=quantile(rep(1,5)) #quantile()是对输入数据的等分
> dvquan3
0% 25% 50% 75% 100%
1 1 1 1 1
eg3.计算数据集thuesen的分位数
>library(ISwR)
>data(thuesen)
> daf=apply(thuesen,2,quantile,na.rm=T) #对thuesen的每一列应用quantile
> daf
Blood.glucose short.velocity
0% 4.200 1.030
25% 7.075 1.185
50% 9.400 1.270
75% 12.700 1.420
100% 19.500 1.950
编程应用1:计算三维数组的分位数
学生成绩数据集的三维数组dar2,计算学生成绩观测的数学期望,可知道学生的综合得分,并且据此进行A~E等级的打分。
> dmean=apply(dar2,c(1,2),mean)
> dmean
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 70.0825 70.5425 67.5275 73.3525 71.2150 71.2375 63.665
[2,] 70.1925 67.2175 68.0775 60.2225 72.2875 69.4275 66.600
为输出每个学生的综合成绩,必须将学号增加到成绩栏,因此构建数据框stuscores,将矩阵dmean转到数据框的变量,注意应保持原来的顺序。
>dv=as.vector(dmean)
>stuscores=data.frame("学号"=dafstu[,1],"综合成绩"=dv)
>stuscores
学生的成绩比较接近,所以用原有的五分制,不能表示学生在班级的学习水平间距离。因此用综合成绩的四分位数表示A~E五个等级,否则可用函数funfactor()完成。
>fator1=quantile(dv,prev=c(.8,.6,.4,.2))
factor1[2:5]=factor1
factor1[1]=100.0
names(factor1)=c("100%","80%","60%","40%","20%")
gradeterm=c("A","B","C","D","E")
for(i in 1:4)
{
gradelog=dv>=factor1[i+1]&dv<factor1[i]
dv[gradelog]=gradeterm[i]
}
dv[dv<factor1[5]]=gradeterm[5]
dv
[1] "C" "B" "B" "D" "D" "D" "A" "E" "B" "A" "A" "C" "E" "E"
length(dv)
> stuscores=cbind(stuscores,grade=dv)
> stuscores
学号 综合成绩 grade
1 3600101 70.0825 C
2 3600102 70.1925 B
3 3600103 70.5425 B
4 3600104 67.2175 D
5 3600105 67.5275 D
6 3600106 68.0775 D
7 3600107 73.3525 A
8 3600108 60.2225 E
9 3600109 71.2150 B
10 3600110 72.2875 A
11 3600111 71.2375 A
12 3600112 69.4275 C
13 3600113 63.6650 E
14 3600114 66.6000 E
3.1.2 lapply(),sapply()和vapply()函数
lapply()和sapply()只能应用在二维数据结构,例如列表的元素,数据框的变量,而且并不需要指定维度。lappy()是最基本的原型函数,不妨知道它是R语言最简单的泛函,仅此而已。lapply(),sapply()和vapply()的两个主要参数是data和f()。data的数据类型是列表或向量,函数对所有列表元素、数据框变量应用f()函数。 lapply()返回的结果是列表,长度与data相同,sapply()返回的结果是向量,矩阵或数组,结果需要做预测。而vapply()函数将对返回结果的值进行类型检查,参数FUN.VALUE设置返回值类型,因此vapply()是结果可预测的sapply()版。所以不可在函数内部用sapply(),而应使用vapply()。lapply()和sapply()可实现数据结构操作的大多数功能,包括创建数据结构、取子集等,然而这并不是它们的优势。访问操作与"["相同,"["可提取数据结构的分量。
1.lapply()的基本应用
eg4. 函数lapply()应用在列表
> stuscores=list(digital.circuit=c(80,88,94,70,86,79,98),analogous.circuit=c(99,87,100,77,83,80,92),electrocircuit=c(82,90,96,80,80,71,95))
#建立列表,以行为单位处理分组数据
>dlist= lapply(stuscores,quantile,probs=c(0.5,0.7,0.9)) #计算0.5,0.7,0.9的分位数
>dlist
$digitalcircuit
50% 70% 90%
86.0 89.2 95.6
$analogouscircuit
50% 70% 90%
87.0 93.4 99.4
$electrocircuit
50% 70% 90%
82.0 91.0 95.4
2.sapply()的基本应用
sapply是对结果进行simplify的lappy(),并且应用在数据框。
eg5.1 sapply()建立数据结构
>dv1=c(1:4)
> dafr1=sapply(dv1,FUN=rep,times=4) #按列建立矩阵
> dafr1
[,1] [,2] [,3] [,4]
[1,] 1 2 3 4
[2,] 1 2 3 4
[3,] 1 2 3 4
[4,] 1 2 3 4
eg5.2 sapply()取子集
> sapply(dar1,function(data) data[4]) #sapply()只能用在数据框
[1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[25] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[49] NA NA NA NA NA NA NA NA
> dv2= sapply(dafrstu[2:5],function(data) data[4]) #取每一列的第四个元素
专业课平均成绩 社会实践大学英语平均成绩 英语四级
79.93 78.83 61.46 48.65
注意不是取数据框data的第四个变量,而是所有列的第四个元素。
> class(dv2) #dv2是向量
[1] "numeric"
> typeof(dv2)
[1] "double"
> mode(dv2)
[1] "numeric"
> str(dv2) #dv2是向量
Named num [1:4] 79.9 78.8 61.5 48.6
- attr(*, "names")= chr [1:4] "专业课平均成绩" "社会实践" "大学英语平均成绩" "英语四级"
eg6. 应用在thuesen数据集
> dim(thuesen)
[1] 24 2
> dvquan1=sapply(thuesen,quantile,na.rm=T) #sapply()应用在数据框的变量
> dvquan1
Blood.glucose short.velocity
0% 4.200 1.030
25% 7.075 1.185
50% 9.400 1.270
75% 12.700 1.420
100% 19.500 1.950
从分位数可知,blood.glucose在4.2和19.5间,50%在9.4以上。short.velocity
在1.03和1.95间,50%在1.27以上。
3.vapply()
函数vapply()的返回值的类型用参数FUN.VALUE预先定义,而参数USE.NAMES 判定data是否有标签,若为无标签(T),则应命名。
eg7.1设置函数vapply()结果类型
用retval变量设置返回值长度和类型,如果FUN函数获得的结果和retval的设置不一致,则返回出错信息。
> probs=c(1:3/4) #四分位数的三个分位数,三个数
> probs
[1] 0.25 0.50 0.75
> retval=c(0,0,0) #设置返回值是向量,有三个元素
> vapply(scores,quantile,FUN.VALUE=retval,probs=probs)
digitalcircuit analogouscircuit electrocircuit
25% 79.5 81.5 80.0
50% 86.0 87.0 82.0
75% 91.0 95.5 92.5
>probs=c(1:4/4) #四分位数,四个数与返回值类型不符
> vapply(scores,quantile,FUN.VALUE=retval,probs=probs)
Error in vapply(scores, quantile, FUN.VALUE = retval, probs = probs) :
值的长度必需为3,
但FUN(X[[1]])结果的长度却是4
因为vapply()检验返回值类型,发现函数的计算结果与返回值类型不符,所以错误。
4.mapply()的应用
函数mapply()用在函数的参数有多个不同值的时候,参数顺序和sapply()不同。标准格式:
mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
data数据类型为向量或列表,函数FUN对元素应用;若参数长度为1,得到的结果和sapply相同;但如果参数长度不等于1,FUN函数将按向量顺序和循环规则(短向量重复)逐个取参数应用到对应数据元素。
eg8.函数mapply() 建立数据结构
> dafr2=sapply(dv1,FUN=rep,times=1:4) #sapply()不能用在函数参数有不同值
Error in FUN(X[[i]], ...) : 'times'参数不对
> dafr2=mapply(dv1,FUN=rep,times=1:4)
> dafr2
[[1]]
[1] 1
[[2]]
[1] 2 2
[[3]]
[1] 3 3 3
[[4]]
[1] 4 4 4 4
3.1.3 tapply()和同类系列函数
tapply()根据一个因子向量对数据向量进行分类,得到的分组数据是不等长分组,然后对每个分组应用函数fun()。tapply()语法格式:
tapply(data, index, FUN = NULL, ..., simplify = TRUE)
data只能是向量,index为因子向量,长度应与data相同。返回值是向量,若simplify=FALSE输出列表。index向量因子有两个形式:1)数据框的变量2)指定的分类向量。可用c()生成不规则的因子,也可用gl()生成等长分类的向量。
同类系列函数有split()和by()。split()函数仅对数据框或一个变量根据另一个变量分组,输出是列表。by()能对数据框和矩阵根据因子分组,可应用多变量函数fun()。
eg9.函数gl()生成分类向量
> data1=c(1:12)
[1] 1 2 3 4 5 6 7 8 9 10 11 12
> factor2=gl(2,6,labels=c("Good","Bad")) #生成两个分类各6个元素的向量
> factor2
factor2
[1] Good Good Good Good Good Good Bad Bad Bad Bad Bad Bad
Levels: Good Bad
>tapply(data1,factor2,length)
Good Bad
6 6
> tapply(data1, factor2, sum)
Good Bad
21 57
这说明坏与西斯一样总比好的绝地有力量速度快,所以伟大导师马克思说人不是靠身体的力量和敏捷、当时的社会地位、别人的毁誉成功,而是靠意志、性格(能力)和知识的力量。
eg10.tapply()的实际应用
mtcars 是不同类型汽车道路测试的数据框类型数据
>str(mtcars)
'data.frame': 32 obs. of 11 variables:
$ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
$ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
$ disp: num 160 160 108 258 360 ...
$ hp : num 110 110 93 110 175 105 245 62 95 123 ...
$ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
$ wt : num 2.62 2.88 2.32 3.21 3.44 ...
$ qsec: num 16.5 17 18.6 19.4 17 ...
$ vs : num 0 0 1 1 0 1 0 1 1 1 ...
$ am : num 1 1 1 0 0 0 0 0 0 0 ...
$ gear: num 4 4 4 3 3 3 3 4 4 4 ...
$ carb: num 4 4 1 1 2 1 4 2 2 4 ...
数据集每加仑汽油行驶里程mpg,气缸规格cyl,马力hp。
(1)计算四缸汽车平均mpg
> tapply(mpg,cyl,mean)
4 6 8
26.66 19.74 15.10