背景:
鸢尾花数据集最初由Edgar Anderson 测量得到,而后在著名的统计学家和生物学家R.A Fisher于1936年发表的文章「The use of multiple measurements in taxonomic problems」中被使用,用其作为线性判别分析(Linear Discriminant Analysis)的一个例子,证明分类的统计方法,从此而被众人所知,尤其是在机器学习这个领域。
数据中的两类鸢尾花记录结果是在加拿大加斯帕半岛上,于同一天的同一个时间段,使用相同的测量仪器,在相同的牧场上由同一个人测量出来的。这是一份有着70年历史的数据,虽然老,但是却很经典,详细数据集可以在UCI数据库中找到。
鸢尾花数据集共收集了三类鸢尾花,即Setosa鸢尾花、Versicolour鸢尾花和Virginica鸢尾花,每一类鸢尾花收集了50条样本记录,共计150条。
1. apply()系列函数
apply()的被分析对象必须且只能是矩阵或数组
apply()对层、行、列、行和列应用函数,根据观测、变量和数据集不同层次的特征决定。语法格式为:
apply(dataset, MARGIN, FUN)
dataset是apply应用的数据集,数据结构是数组、矩阵或数据框。参数MARGIN是apply()应用的维度,MARGIN=1表示矩阵和数组的行,MARGIN=2表示矩阵和数组的列。参数FUN为应用的计算函数f(),可带有f()的参数。FUN函数结果的长度确定apply()的返回值类型,通常为array类型,若返回值的向量长度不等,则返回list对象。
> d<- matrix(1:9, ncol=3)
> d
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
> apply(d, 1, sum)
[1] 12 15 18
使用apply()函数对鸢尾花数据集第1-4列求和,包含所有行。
> 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
> apply(iris[, 1:4],2, sum)
Sepal.Length Sepal.Width Petal.Length Petal.Width
876.5 458.6 563.7 179.9
2. lapply()函数
lapply()的被分析对象必须且只能是向量或列表
apply系列函数中,lapply()函数以列表形式返回应用函数的结果,这是其最显著特征。使用lapply()函数处理数据框后得到列表,有时可能需要将得到的列表再次转为数据框。转换需要经过如下几个阶段:
1.使用unlist()函数,将列表转换为数据框。
2.使用matrix()函数,将想来那个转换为矩阵。
3.使用as.data.frame()函数,将矩阵转换为数据框。
4.使用names()函数,从列表获取变量名,赋给数据框的各列。
对于数据框的行名和列名,可以分别使用rownames()、colnames()函数指定。
> lapply(iris[, 1:4], sum)
$`Sepal.Length`
[1] 876.5
$Sepal.Width
[1] 458.6
$Petal.Length
[1] 563.7
$Petal.Width
[1] 179.9
> lapply(iris[, 1:4], mean)
$`Sepal.Length`
[1] 5.843333
$Sepal.Width
[1] 3.057333
$Petal.Length
[1] 3.758
$Petal.Width
> lapply(split(iris$Sepal.Length,iris$Species), mean)
$`setosa`
[1] 5.006
$versicolor
[1] 5.936
$virginica
[1] 6.588
> unlist(lapply(split(iris$Sepal.Length,iris$Species), mean))
setosa versicolor virginica
5.006 5.936 6.588
3. sapply()函数
sapply()的被分析对象必须且只能是向量或列表
sapply()函数与lapply()函数类似,但以矩阵、向量等数据类型返回结果。下列示例代码中,使用lapply()函数将以列表形式返回各列平均值,而使用sapply()函数则以向量形式返回各列平均值。
sapply(list,g) g为操作函数,返还结果为向量,而lapply返还结果为list形式。常与split结合使用
> sapply(iris[, 1:4], sum)
Sepal.Length Sepal.Width Petal.Length Petal.Width
876.5 458.6 563.7 179.9
> sapply(iris[, 1:4], mean)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
使用as.data.frame()函数可以将sapply()函数返回的向量进一步转换为数据框。此时,需要t(x)函数转置向量的行与列,将无法得到想要的数据框。下列中,先使用sapply()函数对鸢尾花数据集的前四列分别求平均值,得到包含平均值的向量后,再使用as.data.frame()函数将向量转换为数据框。
> x<-sapply(iris[, 1:4], mean)
> as.data.frame(x)
x
Sepal.Length 5.843333
Sepal.Width 3.057333
Petal.Length 3.758000
Petal.Width 1.199333
> as.data.frame(t(x))
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 5.843333 3.057333 3.758 1.199333
> sapply(iris, class)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
"numeric" "numeric" "numeric" "numeric" "factor"
> y<-sapply(iris[, 1:4], function(x){x > 3})
> class(y)
[1] "matrix"
> head(y)
Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,] TRUE TRUE FALSE FALSE
[2,] TRUE FALSE FALSE FALSE
[3,] TRUE TRUE FALSE FALSE
[4,] TRUE TRUE FALSE FALSE
[5,] TRUE TRUE FALSE FALSE
[6,] TRUE TRUE FALSE FALSE
4. tapply()函数
apply系列函数中,使用tapply()函数会先对数据进行分组,然后将函数应用到各组。
参考帖子:http://rsoftware.h.baike.com/article-2015511.html
tapply(x,f,g) x为向量,f为因子列,g为操作函数,相对数据框进行类似操作可以用by函数.
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()。
> daf1<-data.frame(gender=c("M","M","F","M","F","F","M"),
+ age=c(47,59,21,32,40,24,25),
+ salary=c(55000,88000,32450,76500,123000,45650,65000)
+ )
> daf1$over40=ifelse(daf1$age>40,1,0) #年龄大于40岁的员工
> daf1$over40
[1] 1 1 0 0 0 0 0
> tapply(daf1$salary,list(daf1$gender,daf1$over40),mean) #计算平均工资
0 1
F 67033.33 NA
M 70750.00 71500
> #用list()设置多个因子,将工资salary分成四组,所以有4个答案。
> tapply(daf1$salary,c(daf1$gender,daf1$over40),mean)
Error in tapply(daf1$salary, c(daf1$gender, daf1$over40), mean) :
arguments must have same length
# 参数的长度必需相同, 参数长度不同,是因为c()的连接作用。
> df <- data.frame(year=kronecker(2001:2003, rep(1,4)),
+ loc=c('beijing','beijing','shanghai','shanghai'),
+ type=rep(c('A','B'),6),
+ sale=rep(1:12))
> df
year loc type sale
1 2001 beijing A 1
2 2001 beijing B 2
3 2001 shanghai A 3
4 2001 shanghai B 4
5 2002 beijing A 5
6 2002 beijing B 6
7 2002 shanghai A 7
8 2002 shanghai B 8
9 2003 beijing A 9
10 2003 beijing B 10
11 2003 shanghai A 11
12 2003 shanghai B 12
> tapply(df$sale, df[,c('year','loc')], sum)
loc
year beijing shanghai
2001 3 7
2002 11 15
2003 19 23
> tapply(df$sale, df[,c('year','type')], sum)
type
year A B
2001 4 6
2002 12 14
2003 20 22
5. mapply()函数
mapply()的被分析对象必须是函数
mapply()函数与 sapply()函数类似,但他可以将多个参数传递给指定函数。 mapply()函数的第一个参数是待应用的FUN函数,它接受多个参数。要传递给FUN()函数的参数作为数据保存时,mapply()函数将保存在数据中的值转换为参数,传递给FUN函数,并调用执行FUN函数。
函数mapply()用在函数的参数有多个不同值的时候,参数顺序和sapply()不同。标准格式:
mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
data数据类型为向量或列表,函数FUN对元素应用;若参数长度为1,得到的结果和sapply相同;但如果参数长度不等于1,FUN函数将按向量顺序和循环规则(短向量重复)逐个取参数应用到对应数据元素。
使用mapply()函数时,如果给定多个数据,则这些数据的第一个元素组成一组,作为参数传递给FUN函数;然后第二个元素组成一组,作为参数传递给FUN函数,以此类推。
> dv1=c(1:4)
> 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
下面使用mapply()函数,计算鸢尾花数据集1-4列的平均值与总和。
> data("iris")
> mapply(mean,iris[, 1:4])
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
> mapply(sum,iris[, 1:4])
Sepal.Length Sepal.Width Petal.Length Petal.Width
876.5 458.6 563.7 179.9
图片来源:https://blog.csdn.net/wzgl__wh/article/details/52207233
6. vapply()函数应用
vapply按变量进行函数操作,vapply类似于sapply函数,但是它的返回值有预定义类型,所以它使用起来会更加安全,有的时候会更快。在vapply函数中总是会进行简化,vapply会检测FUN的所有值是否与FUN.VALUE兼容,以使他们具有相同的长度和类型。类型顺序:逻辑、整型、实数、复数。
vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)
X表示一个向量或者表达式对象,其余对象将被通过as.list强制转换为list
simplify 逻辑值或者字符串,如果可以,结果应该被简化为向量、矩阵或者高维数组。
必须是命名的,不能是简写。默认值是TRUE,若合适将会返回一个向量或者矩阵。如果simplify=”array”,结果将返回一个阵列。
USE.NAMES 逻辑值,如果为TRUE,且x没有被命名,则对x进行命名。
FUN.VALUE 一个通用型向量,FUN函数返回值得模板。
x<-data.frame(a=rnorm(4,4,4),
b=rnorm(4,5,3),
c=rnorm(4,5,3))
vapply(x,mean,c(c=0))
k<-function(x){
list(mean(x),sd(x))
}
vapply(x,k,c(list(c=0,b=0)))
a b c
c 3.495029 2.780169 5.41083
b 4.671292 4.283595 3.320549
7. eapply 函数table
eapply函数通过对environment中命名值进行FUN计算后返回一个列表值,用户可以请求所有使用过的命名对象。
eapply(env, FUN, ..., all.names = FALSE, USE.NAMES = TRUE)
env 将被使用的环境
all.names 逻辑值,指示是否对所有值使用该函数
USE.NAMES 逻辑值,指示返回的列表结果是否包含命名
> require(stats)
> env <- new.env(hash = FALSE) # so the order is fixed
> env$a <- 1:10
> env$beta <- exp(-3:3)
> utils::ls.str(env)
a : int [1:10] 1 2 3 4 5 6 7 8 9 10
beta : num [1:7] 0.0498 0.1353 0.3679 1 2.7183 ...
> eapply(env, mean)
$`beta`
[1] 4.535125
$a
[1] 5.5
> unlist(eapply(env, mean, USE.NAMES = FALSE))
[1] 4.535125 5.500000
> eapply(env, quantile, probs = 1:3/4)
$`beta`
25% 50% 75%
0.2516074 1.0000000 5.0536690
$a
25% 50% 75%
3.25 5.50 7.75
> eapply(env, quantile)
$`beta`
0% 25% 50% 75% 100%
0.04978707 0.25160736 1.00000000 5.05366896 20.08553692
$a
0% 25% 50% 75% 100%
1.00 3.25 5.50 7.75 10.00
8、rapply 函数
rapply是lapply的递归版本, 它只处理list类型数据,对list的每个元素进行递归遍历,如果list包括子元素则继续遍历。
rapply(object, f, classes = "ANY", deflt = NULL, how = c("unlist", "replace", "list"), ...)
参数列表:
object:list数据
f: 自定义的调用函数
classes : 匹配类型, ANY为所有类型
deflt: 非匹配类型的默认值
how: 3种操作方式,当为replace时,则用调用f后的结果替换原list中原来的元素;当为list时,新建一个list,类型匹配调用f函数,不匹配赋值为deflt;当为unlist时,会执行一次unlist(recursive = TRUE)的操作
…: 更多参数,可选
比如,对一个list的数据进行过滤,把所有数字型numeric的数据进行从小到大的排序。
> x=list(a=12,b=1:4,c=c('b','a'))
> y=pi
> z=data.frame(a=rnorm(10),b=1:10)
> a <- list(x=x,y=y,z=z)
> # 进行排序,并替换原list的值
> rapply(a,sort, classes='numeric',how='replace')
$`x`
$`x`$`a`
[1] 12
$`x`$b
[1] 1 2 3 4
$`x`$c
[1] "b" "a"
$y
[1] 3.141593
$z
a b
1 -1.8383682 1
2 -1.0000541 2
3 -0.9739195 3
4 -0.9118907 4
5 -0.8726053 5
6 -0.7946537 6
7 -0.6531306 7
8 -0.6397726 8
9 -0.3413240 9
10 1.0768179 10
> class(a$z$b)
[1] "integer"
从结果发现,只有$z$a
的数据进行了排序,检查$z$b
的类型,发现是integer,是不等于numeric的,所以没有进行排序。
接下来,对字符串类型的数据进行操作,把所有的字符串型加一个字符串’++++’,非字符串类型数据设置为NA。
> rapply(a,function(x) paste(x,'++++'),classes="character",deflt=NA, how = "list")
$`x`
$`x`$`a`
[1] NA
$`x`$b
[1] NA
$`x`$c
[1] "b ++++" "a ++++"
$y
[1] NA
$z
$z$`a`
[1] NA
$z$b
[1] NA
只有$x$c
为字符串向量,都合并了一个新字符串。那么,有了rapply就可以对list类型的数据进行方便的数据过滤了。
9. 函数table(求因子出现的频数)
使用格式为:
table(..., exclude = if (useNA == "no") c(NA, NaN), useNA = c("no", "ifany", "always"), dnn = list.names(...), deparse.level = 1)
其中参数exclude表示哪些因子不计算。
示例代码:
> d <- factor(rep(c("A","B","C"), 5), levels=c("A","B","C","D","E"))
> d
[1] A B C A B C A B C A B C A B C
Levels: A B C D E
> table(d)
d
A B C D E
5 5 5 0 0
> table(d, exclude="B")
d
A C D E
5 5 0 0
常用apply族函数对比
lapply()和sapply()只能应用在二维数据结构,例如列表的元素,数据框的变量,而且并不需要指定维度。lappy()是最基本的原型函数,不妨知道它是R语言最简单的泛函,仅此而已。lapply(),sapply()和vapply()的两个主要参数是data和f()。data的数据类型是列表或向量,函数对所有列表元素、数据框变量应用f()函数。 lapply()返回的结果是列表,长度与data相同,sapply()返回的结果是向量,矩阵或数组,结果需要做预测。而vapply()函数将对返回结果的值进行类型检查,参数FUN.VALUE设置返回值类型,因此vapply()是结果可预测的sapply()版。所以不可在函数内部用sapply(),而应使用vapply()。lapply()和sapply()可实现数据结构操作的大多数功能,包括创建数据结构、取子集等,然而这并不是它们的优势。访问操作与"["相同,"["可提取数据结构的分量。
参考:
【1】R语言与数据分析实战 徐珉久,武传海著,人民邮电出版社
【2】第三章第一节 apply族函数 http://rsoftware.h.baike.com/article-2015511.html
【3】R语言中的lapply sapply apply tapply mapply http://iccm.cc/lapply-sapply-apply-tapply-mapply-in-r-language/
【4】R中的apply族函数 https://zhuanlan.zhihu.com/p/26466130
【5】R语言︱数据分组统计函数族——apply族用法与心得: https://blog.csdn.net/sinat_26917383/article/details/51086663
【6】掌握R语言中的apply函数族 http://blog.fens.me/r-apply/
生信技能树公益视频合辑:学习顺序是linux,r,软件安装,geo,小技巧,ngs组学!
B站链接:https://m.bilibili.com/space/338686099
YouTube链接:https://m.youtube.com/channel/UC67sImqK7V8tSWHMG8azIVA/playlists
生信工程师入门最佳指南:https://mp.weixin.qq.com/s/vaX4ttaLIa19MefD86WfUA
学徒培养:https://mp.weixin.qq.com/s/3jw3_PgZXYd7FomxEMxFmw
生信技能树 - 简书 https://www.jianshu.com/u/d645f768d2d5