R语言基础6--apply函数家族及其应用


R语言基础系列:

  • R语言基础1--R数据格式:.rds和.rda的区别
  • R语言基础2--数据排序与长宽型数据的转换
  • R语言基础3--tidyverse包总结
  • R语言基础4--dplyr包的函数及用法
  • R语言基础5--tidyr包的函数及用法
  • apply函数家族是R语言中数据处理的一组核心函数,通过使用apply函数,我们可以实现对数据的循环、分组、过滤、类型控制等操作。
    apply函数本身是用来解决数据循环处理的问题,为了面向不同的数据类型,不同的返回值,apply函数组成了一个函数加族,包括了8个功能类似的函数。

    下面将分别介绍这几个函数的定义和使用方法。

    1. apply函数

    apply函数是最常用的代替for循环的函数。apply函数可以对 矩阵 数据框 数组 (二维、多维), \color{#ea4335}{按行或列进行循环计算} ,对子元素进行迭代,并把子元素以参数传递的形式给自定义的FUN函数中,并以返回计算结果。

    1.1 函数定义:
    apply(X, MARGIN, FUN, ...)
    
    1.2 参数列表:

    X:数组、矩阵、数据框
    MARGIN:按行计算或按按列计算,1表示按行,2表示按列
    FUN:自定义的调用函数
    …:更多参数,可选

    1.3 应用:
    # 对一个矩阵的每一列求和
    # 生成data.frame
    > x <- cbind(x1 = 3, x2 = c(4:1, 2:5)); x
         x1 x2
    [1,]  3  4
    [2,]  3  3
    [3,]  3  2
    [4,]  3  1
    [5,]  3  2
    [6,]  3  3
    [7,]  3  4
    [8,]  3  5
    apply(x,2,sum)
    # x1 x2 
    # 24 24 
    apply(x,1,mean)
    # [1] 3.5 3.0 2.5 2.0 2.5 3.0 3.5 4.0
    

    2. lapply函数

    lapply函数是一个最基础循环操作函数之一,用来对listdata.frame数据集进行循环,并\color{#ea4335}{返}\color{#ea4335}{回}\color{#ea4335}{和}\color{#ea4335}{X}\color{#ea4335}{长}\color{#ea4335}{度}\color{#ea4335}{相}\color{#ea4335}{同}\color{#ea4335}{的}\color{#ea4335}{list}\color{#ea4335}{结}\color{#ea4335}{构}作为结果集,通过lapply的开头的第一个字母’l’就可以判断返回结果集的类型。

    2.1 函数定义
    lapply(X, FUN, ...)
    
    2.2 参数列表

    X:list、data.frame数据
    FUN:自定义的调用函数
    …:更多参数,可选

    2.3 应用

    输入list,返回对每个list进行操作后的list。

    # 构建一个list数据集x,分别包括a,b,c 三个KEY值。
    x <- list(a = 1:10, b = rnorm(6,10,5), c = c(TRUE,FALSE,FALSE,TRUE));x
    #  [1]  1  2  3  4  5  6  7  8  9 10
    # [1]  9.186837  7.372569  9.942975 16.946282  2.550059
    # [6]  6.991188
    # [1]  TRUE FALSE FALSE  TRUE
    # 分别计算每个KEY对应该的数据的分位数。
    lapply(x,fivenum)
    # [1]  1.0  3.0  5.5  8.0 10.0
    # [1]  2.550059  6.991188  8.279703  9.942975 16.946282
    # [1] 0.0 0.0 0.5 1.0 1.0
    

    如果输入的是数据框,lapply会自动把数据框按列进行分组,再进行计算。

    lapply(data.frame(x), sum)
    [1] 12
    [1] 12
    

    lapply可以很方便地对list数据集中个每个list进行循环操作,还可以用data.frame数据集按列进行循环,但如果传入的数据集是一个向量或矩阵对象,那么直接使用lapply就不能达到想要的效果了。lapply会分别循环矩阵中的每个值,而不是按行或按列进行分组计算。

    x <- cbind(x1=3, x2=c(2:1,4:5))
    x; class(x)
    #      x1 x2
    # [1,]  3  2
    # [2,]  3  1
    # [3,]  3  4
    # [4,]  3  5
    # [1] "matrix" "array" 
    lapply(x, sum)
    # [[1]]
    # [1] 3
    # [[2]]
    # [1] 3
    # [[3]]
    # [1] 3
    # [[4]]
    # [1] 3
    # [[5]]
    # [1] 2
    # [[6]]
    # [1] 1
    # [[7]]
    # [1] 4
    # [[8]]
    # [1] 5
    

    3. sapply函数

    sapply函数是一个简化版的lapply,sapply增加了2个参数simplify和USE.NAMES,主要就是让输出看起来更友好,与lapply不同的是,\color{#ea4335}{sapply返回值}\color{#ea4335}{为向量、}\color{#ea4335}{矩阵或数据框},而不是list对象。

    3.1 函数定义
    sapply(X, FUN, ..., simplify=TRUE, USE.NAMES = TRUE)
    
    3.2 参数列表

    X:数组、矩阵、数据框
    FUN:自定义的调用函数
    …:更多参数,可选
    simplify:是否数组化,当设置simplify='array'时,输出结果按数组进行分组
    USE.NAMES:如果X为字符串,TRUE设置字符串为数据名,FALSE不设置

    3.3 应用
    x <- cbind(x1=3, x2=c(2:1,4:5))
    # 对矩阵计算,计算过程同lapply函数
    sapply(x, sum)
    # [1] 3 3 3 3 2 1 4 5
    # 对数据框计算
    sapply(data.frame(x), sum)
    # x1 x2 
    # 12 12 
    # 检查结果类型,sapply返回类型为向量,而lapply的返回类型为list
    class(lapply(x, sum))
    # [1] "list"
    class(sapply(x, sum))
    # [1] "numeric"
    

    如果simplify=FALSE和USE.NAMES=FALSE,那么完全sapply函数就等于lapply函数了。

    sapply(data.frame(x), sum, simplify=FALSE, USE.NAMES=FALSE)
    # $x1
    # [1] 12
    # $x2
    # [1] 12
    

    对于simplify为array时,我们可以参考下面的例子,构建一个三维数组,其中二个维度为方阵。

    a<-1:2
    # 按数组分组
    sapply(a,function(x) matrix(x,2,2), simplify='array')
    # , , 1
    #      [,1] [,2]
    # [1,]    1    1
    # [2,]    1    1
    # , , 2
    #      [,1] [,2]
    # [1,]    2    2
    # [2,]    2    2
    # 默认情况,则自动合并分组
    sapply(a,function(x) matrix(x,2,2))
    #      [,1] [,2]
    # [1,]    1    2
    # [2,]    1    2
    # [3,]    1    2
    # [4,]    1    2
    

    对于字符串的向量,还可以自动生成数据名。

    val<-head(letters)
    # 默认设置数据名
    sapply(val,paste,USE.NAMES=TRUE)
    #   a   b   c   d   e   f 
    # "a" "b" "c" "d" "e" "f" 
    # USE.NAMES=FALSE,则不设置数据名
    sapply(val,paste,USE.NAMES=FALSE)
    # [1] "a" "b" "c" "d" "e" "f"
    

    4. tapply函数

    tapply用于分组的循环计算,通过INDEX参数(分类变量)对数据集X进行分组并计算分组计算,相当于group by的操作。

    4.1 函数定义
    tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)
    
    4.2 参数列表
    INDEX:用于分组的索引
    FUN:自定义的调用函数
    …:接收多个数据
    simplify:是否数组化,当值array时,输出结果按数组进行分组

    4.3 应用
    #计算不同品种(Species)的鸢尾花(iris)的花瓣长度(Petal.Length)的均值。
    tapply(X = iris$Petal.Length,INDEX = iris$Species,FUN = mean)
    #    setosa versicolor  virginica 
    #     1.462      4.260      5.552 
    

    5. mapply函数

    mapply也是sapply的变形函数,类似多变量的sapply,但是参数定义有些变化。第一参数为自定义的FUN函数,第二个参数’…’可以接收多个数据,作为FUN函数的参数调用。

    5.1 函数定义
    mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE,USE.NAMES = TRUE)
    
    5.2 参数列表

    FUN:自定义的调用函数
    …:接收多个数据
    MoreArgs:参数列表
    SIMPLIFY:是否数组化,当值array时,输出结果按数组进行分组
    USE.NAMES:如果X为字符串,TRUE设置字符串为数据名,FALSE不设置

    5.3 应用

    比如,比较3个向量大小,按索引顺序取较大的值。

    set.seed(1)
    # 定义3个向量
    x<-1:10
    y<-5:-4
    z<-round(runif(10,-5,5))
    # 按索引顺序取较大的值。
    mapply(max,x,y,z)
    # [1]  5  4  3  4  5  6  7  8  9 10
    

    再看一个例子,生成4个符合正态分布的数据集,分别对应的均值和方差为c(1,10,100,1000)。

    set.seed(1)
    # 长度为4
    n<-rep(4,4)
    # m为均值,v为方差
    m<-v<-c(1,10,100,1000)
    # 生成4组数据,按列分组
    mapply(rnorm,n,m,v)
    #           [,1]      [,2]      [,3]       [,4]
    # [1,] 0.3735462 13.295078 157.57814   378.7594
    # [2,] 1.1836433  1.795316  69.46116 -1214.6999