apply()族函数- 土豆R语言与数据分析实战

作者: 土豆学生信 | 来源:发表于2019-01-02 12:59 被阅读14次

    背景:

    鸢尾花数据集最初由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
    
    image.png
    图片来源: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族函数对比

    image.png

    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

    相关文章

      网友评论

        本文标题:apply()族函数- 土豆R语言与数据分析实战

        本文链接:https://www.haomeiwen.com/subject/mmlhlqtx.html