关于r:分组函数(tapply,by,aggregate)和* apply系列

Grouping functions (tapply, by, aggregate) and the *apply family

每当我想在R中做"map"py时,我通常会尝试在apply系列中使用一个函数。

但是,我从来没有完全理解它们之间的区别 - {sapplylapply等}如何将函数应用于输入/分组输入,输出将是什么样的,甚至输入可以是什么所以我经常只是经历它们直到我得到我想要的东西。

有人可以解释如何使用哪一个?

我当前(可能不正确/不完整)的理解是......

  • sapply(vec, f):输入是向量。输出是矢量/矩阵,其中元素if(vec[i]),如果f具有多元素输出,则为您提供矩阵

  • lapply(vec, f):与sapply相同,但输出是一个列表?

  • apply(matrix, 1/2, f):输入是一个矩阵。 output是一个向量,其中element i是f(矩阵的row / col i)
  • tapply(vector, grouping, f):输出是矩阵/阵列,其中所述基质/数组中的元素是<5233>的值在一组g载体,并g被推到行/列名
  • by(dataframe, grouping, f):让g成为分组。将f应用于组/数据帧的每一列。在每列打印分组和f的值。
  • aggregate(matrix, grouping, f):类似于by,但是聚合不是将输出完全打印到数据帧中。
  • 附带问题:我还没有学过plyr或重塑 - 将plyrreshape完全取代所有这些?


    R具有许多*应用功能,这些功能在帮助文件中有很好的描述(例如?apply)。但是,他们已经足够了,开始使用R可能很难决定哪一个适合他们的情况甚至记住它们。他们可能会有一个普遍的感觉,"我应该在这里使用* apply函数",但最初要保持一致是很困难的。

    尽管事实(在其他答案中指出)* apply系列的大部分功能都被极受欢迎的plyr包覆盖,但基本功能仍然有用且值得了解。

    这个答案旨在作为一种新的useRs的路标,以帮助指导他们针对他们的特定问题正确的*应用功能。注意,这不是为了简单地反刍或替换R文档!希望这个答案可以帮助您确定哪种*应用功能适合您的情况,然后由您来进一步研究。除了一个例外,性能差异将无法解决。

  • apply - 当您要将函数应用于行或列时
    矩阵(和更高维的类似物);通常不建议使用数据帧,因为它会首先强制转换为矩阵。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    # Two dimensional matrix
    M <- matrix(seq(1,16), 4, 4)

    # apply min to rows
    apply(M, 1, min)
    [1] 1 2 3 4

    # apply max to columns
    apply(M, 2, max)
    [1]  4  8 12 16

    # 3 dimensional array
    M <- array( seq(32), dim = c(4,4,2))

    # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension
    apply(M, 1, sum)
    # Result is one-dimensional
    [1] 120 128 136 144

    # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension
    apply(M, c(1,2), sum)
    # Result is two-dimensional
         [,1] [,2] [,3] [,4]
    [1,]   18   26   34   42
    [2,]   20   28   36   44
    [3,]   22   30   38   46
    [4,]   24   32   40   48

    如果您想要2D矩阵的行/列平均值或总和,请务必
    调查高度优化,闪电般快速colMeans
    rowMeanscolSumsrowSums

  • lapply - 当你想将一个函数应用于a的每个元素时
    依次列出并获取列表。

    这是许多其他*应用功能的主力。剥
    支持他们的代码,你经常会在下面找到lapply

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    x <- list(a = 1, b = 1:3, c = 10:100)
    lapply(x, FUN = length)
    $a
    [1] 1
    $b
    [1] 3
    $c
    [1] 91
    lapply(x, FUN = sum)
    $a
    [1] 1
    $b
    [1] 6
    $c
    [1] 5005
  • sapply - 当你想将一个函数应用于a的每个元素时
    反过来列出,但你想要一个向量,而不是一个列表。

    如果您发现自己键入unlist(lapply(...)),请停止并考虑
    sapply

    1
    2
    3
    4
    5
    6
    7
    8
    9
    x <- list(a = 1, b = 1:3, c = 10:100)
    # Compare with above; a named vector, not a list
    sapply(x, FUN = length)  
    a  b  c  
    1  3 91

    sapply(x, FUN = sum)  
    a    b    c    
    1    6 5005

    sapply的更高级用途中,它将试图强制使用
    如果合适,结果为多维数组。例如,如果我们的函数返回相同长度的向量,sapply将使用它们作为矩阵的列:

    1
    sapply(1:5,function(x) rnorm(3,x))

    如果我们的函数返回一个二维矩阵,sapply将基本上做同样的事情,将每个返回的矩阵视为一个长向量:

    1
    sapply(1:5,function(x) matrix(x,2,2))

    除非我们指定simplify ="array",否则它将使用各个矩阵来构建一个多维数组:

    1
    sapply(1:5,function(x) matrix(x,2,2), simplify ="array")

    这些行为中的每一个当然都取决于我们的函数返回相同长度或维度的向量或矩阵。

  • vapply - 当你想使用sapply但可能需要时
    从代码中挤出更多速度。

    对于vapply,你基本上给R一个什么样的东西的例子
    你的函数将返回,这可以节省一些时间强制返回
    值适合单个原子向量。

    1
    2
    3
    4
    5
    6
    7
    8
    x <- list(a = 1, b = 1:3, c = 10:100)
    #Note that since the advantage here is mainly speed, this
    # example is only for illustration. We're telling R that
    # everything returned by length() should be an integer of
    # length 1.
    vapply(x, FUN = length, FUN.VALUE = 0L)
    a  b  c  
    1  3 91
  • mapply - 当你有几个数据结构时(例如
    向量,列表)并且您想要将函数应用于第1个元素
    每个,然后是每个的第二个元素等,强制结果
    sapply中的向量/数组。

    在您的功能必须接受的意义上,这是多变量的
    多个参数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #Sums the 1st elements, the 2nd elements, etc.
    mapply(sum, 1:5, 1:5, 1:5)
    [1]  3  6  9 12 15
    #To do rep(1,4), rep(2,3), etc.
    mapply(rep, 1:4, 4:1)  
    [[1]]
    [1] 1 1 1 1

    [[2]]
    [1] 2 2 2

    [[3]]
    [1] 3 3

    [[4]]
    [1] 4
  • Map - mapply的包装器,带有SIMPLIFY = FALSE,因此可以保证返回列表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Map(sum, 1:5, 1:5, 1:5)
    [[1]]
    [1] 3

    [[2]]
    [1] 6

    [[3]]
    [1] 9

    [[4]]
    [1] 12

    [[5]]
    [1] 15
  • rapply - 当您想要递归地将函数应用于嵌套列表结构的每个元素时。

    为了让您了解rapply是多么罕见,我在第一次发布此答案时就忘记了!显然,我相信很多人都会使用它,但是YMMV。使用用户定义的函数最好地说明rapply

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # Append ! to string, otherwise increment
    myFun <- function(x){
        if(is.character(x)){
          return(paste(x,"!",sep=""))
        }
        else{
          return(x + 1)
        }
    }

    #A nested list structure
    l <- list(a = list(a1 ="Boo", b1 = 2, c1 ="Eeek"),
              b = 3, c ="Yikes",
              d = list(a2 = 1, b2 = list(a3 ="Hey", b3 = 5)))


    # Result is named vector, coerced to character          
    rapply(l, myFun)

    # Result is a nested list like l, with values altered
    rapply(l, myFun, how="replace")
  • tapply - 当你想要一个函数应用于a的子集时
    向量和子集由一些其他向量定义,通常是a
    因子。

    *的黑羊适用于各种各样的家庭。帮助文件的使用
    短语"衣衫褴褛的阵列"可能有点令人困惑,但实际上却是这样
    非常简单。

    矢量:

    1
    x <- 1:20

    定义组的因子(长度相同!):

    1
    y <- factor(rep(letters[1:5], each = 4))

    y定义的每个子组中的x中添加值:

    1
    2
    3
    tapply(x, y, sum)  
     a  b  c  d  e  
    10 26 42 58 74

    可以在定义子组的地方处理更复杂的示例
    通过几个因素列表的独特组合。 tapply
    在精神上类似于split-apply-combine功能
    常见于R(aggregatebyaveddply等)因此它
    黑羊身份。

  • 好。


    在旁注中,这里是各种plyr函数如何对应于基本*apply函数(从plyr网页的介绍到plyr文档http://had.co.nz/plyr/)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Base function   Input   Output   plyr function
    ---------------------------------------
    aggregate        d       d       ddply + colwise
    apply            a       a/l     aaply / alply
    by               d       l       dlply
    lapply           l       l       llply  
    mapply           a       a/l     maply / mlply
    replicate        r       a/l     raply / rlply
    sapply           l       a       laply

    plyr的目标之一是为每个函数提供一致的命名约定,对函数名中的输入和输出数据类型进行编码。它还提供输出的一致性,因为dlply()的输出很容易通过ldply()以产生有用的输出等。

    从概念上讲,学习plyr并不比理解基本*apply函数困难。

    plyrreshape函数在我的日常使用中已经取代了几乎所有这些函数。但是,从介绍到Plyr文件:

    Related functions tapply and sweep have no corresponding function in plyr, and remain useful. merge is useful for combining summaries with the original data.


    来自http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy的幻灯片21:

    apply, sapply, lapply, by, aggregate

    (希望很明显apply对应@Hadley的aaplyaggregate对应@Hadley的ddply等。如果你没有从这张图片中得到它,那么同一幻灯片的幻灯片20将会澄清。)

    (左边是输入,顶部是输出)


    首先从Joran的优秀答案开始 - 怀疑任何事情都可以更好。

    然后,以下助记符可能有助于记住每个之间的区别。虽然有些是显而易见的,但有些可能不那么明显 - 对于这些,你会在Joran的讨论中找到理由。

    助记符

    • lapply是列表应用,它作用于列表或向量并返回列表。
    • sapply是一个简单的lapply(函数默认为在可能的情况下返回向量或矩阵)
    • vapply是经过验证的申请(允许预先指定退货对象类型)
    • rapply是嵌套列表的递归应用,即列表中的列表
    • tapply是标记的应用,其中标签标识子集
    • apply是通用的:将函数应用于矩阵的行或列(或更一般地,应用于数组的维度)

    建立正确的背景

    如果使用apply系列仍然感觉有点陌生,那么可能是你错过了一个关键的观点。

    这两篇文章可以提供帮助。它们提供了必要的背景,以激发apply系列函数提供的函数式编程技术。

    Lisp的用户将立即认识到这种范式。如果你不熟悉Lisp,一旦你了解了FP,你将获得一个强大的观点用于R - 并且apply会更有意义。

    • 高级R:功能编程,由Hadley Wickham撰写
    • R的简单函数式编程,作者:Michael Barton

    因为我意识到这篇文章的(非常优秀的)答案缺乏byaggregate的解释。这是我的贡献。

    通过

    by函数,如文档中所述,可以作为tapply的"包装器"。当我们想要计算tapply无法处理的任务时,会产生by的力量。一个例子是这段代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    ct <- tapply(iris$Sepal.Width , iris$Species , summary )
    cb <- by(iris$Sepal.Width , iris$Species , summary )

     cb
    iris$Species: setosa
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
      2.300   3.200   3.400   3.428   3.675   4.400
    --------------------------------------------------------------
    iris$Species: versicolor
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
      2.000   2.525   2.800   2.770   3.000   3.400
    --------------------------------------------------------------
    iris$Species: virginica
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
      2.200   2.800   3.000   2.974   3.175   3.800


    ct
    $setosa
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
      2.300   3.200   3.400   3.428   3.675   4.400

    $versicolor
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
      2.000   2.525   2.800   2.770   3.000   3.400

    $virginica
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
      2.200   2.800   3.000   2.974   3.175   3.800

    如果我们打印这两个对象ctcb,我们"基本上"具有相同的结果,唯一的区别在于它们的显示方式和不同的class属性,分别为bycb array表示ct

    正如我所说,当我们不能使用tapply时,by的力量就会出现;以下代码是一个例子:

    1
    2
    3
     tapply(iris, iris$Species, summary )
    Error in tapply(iris, iris$Species, summary) :
      arguments must have same length

    R表示参数必须具有相同的长度,例如"我们想要计算irisSpecies中所有变量的summary":但是R不能这样做,因为它不知道如何处理。

    使用by函数,R为data frame类调度一个特定的方法,然后让summary函数工作,即使第一个参数的长度(和类型也是)不同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    bywork <- by(iris, iris$Species, summary )

    bywork
    iris$Species: setosa
      Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
     Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100   setosa    :50  
     1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200   versicolor: 0  
     Median :5.000   Median :3.400   Median :1.500   Median :0.200   virginica : 0  
     Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246                  
     3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300                  
     Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600                  
    --------------------------------------------------------------
    iris$Species: versicolor
      Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
     Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
     1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
     Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
     Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
     3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
     Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  
    --------------------------------------------------------------
    iris$Species: virginica
      Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
     Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400   setosa    : 0  
     1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800   versicolor: 0  
     Median :6.500   Median :3.000   Median :5.550   Median :2.000   virginica :50  
     Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026                  
     3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300                  
     Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500

    它确实有效,结果非常令人惊讶。类by的一个对象是沿Species(例如,对于它们中的每一个)计算每个变量的summary

    请注意,如果第一个参数是data frame,则调度函数必须具有该类对象的方法。例如,我们将此代码与mean函数一起使用,我们将拥有完全没有意义的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     by(iris, iris$Species, mean)
    iris$Species: setosa
    [1] NA
    -------------------------------------------
    iris$Species: versicolor
    [1] NA
    -------------------------------------------
    iris$Species: virginica
    [1] NA
    Warning messages:
    1: In mean.default(data[x, , drop = FALSE], ...) :
      argument is not numeric or logical: returning NA
    2: In mean.default(data[x, , drop = FALSE], ...) :
      argument is not numeric or logical: returning NA
    3: In mean.default(data[x, , drop = FALSE], ...) :
      argument is not numeric or logical: returning NA

    骨料

    如果我们以这种方式使用它,aggregate可以被视为另一种不同的使用方式tapply

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    at <- tapply(iris$Sepal.Length , iris$Species , mean)
    ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean)

     at
        setosa versicolor  virginica
         5.006      5.936      6.588
     ag
         Group.1     x
    1     setosa 5.006
    2 versicolor 5.936
    3  virginica 6.588

    两个直接的区别是aggregate的第二个参数必须是一个列表,而tapply可以(不是必需的)是一个列表,aggregate的输出是一个数据帧,而tapply的一个是array

    aggregate的强大之处在于它可以使用subset参数轻松处理数据的子集,并且它还具有ts对象和formula的方法。

    在某些情况下,这些元素使aggregate更容易使用tapply
    以下是一些示例(可在文档中找到):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ag <- aggregate(len ~ ., data = ToothGrowth, mean)

     ag
      supp dose   len
    1   OJ  0.5 13.23
    2   VC  0.5  7.98
    3   OJ  1.0 22.70
    4   VC  1.0 16.77
    5   OJ  2.0 26.06
    6   VC  2.0 26.14

    我们可以用tapply实现相同的功能,但语法稍微难一点,输出(在某些情况下)可读性较差:

    1
    2
    3
    4
    5
    6
    7
    att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)

     att
           OJ    VC
    0.5 13.23  7.98
    1   22.70 16.77
    2   26.06 26.14

    还有一些时候我们不能使用bytapply,我们必须使用aggregate

    1
    2
    3
    4
    5
    6
    7
    8
    9
     ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)

     ag1
      Month    Ozone     Temp
    1     5 23.61538 66.73077
    2     6 29.44444 78.22222
    3     7 59.11538 83.88462
    4     8 59.96154 83.96154
    5     9 31.44828 76.89655

    我们无法在一次调用中使用tapply获取先前的结果,但我们必须计算每个元素的Month的平均值,然后将它们组合起来(还要注意我们必须调用na.rm = TRUE,因为formula方法aggregate函数默认为na.action = na.omit):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ta1 <- tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE)
    ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE)

     cbind(ta1, ta2)
           ta1      ta2
    5 23.61538 65.54839
    6 29.44444 79.10000
    7 59.11538 83.90323
    8 59.96154 83.96774
    9 31.44828 76.90000

    而使用by我们实际上无法实现,实际上以下函数调用返回错误(但很可能它与提供的函数mean有关):

    1
    by(airquality[c("Ozone","Temp")], airquality$Month, mean, na.rm = TRUE)

    其他时候结果是相同的,差异只是在类中(然后它是如何显示/打印的,而不仅仅是 - 例如,如何将其子集化)对象:

    1
    2
    byagg <- by(airquality[c("Ozone","Temp")], airquality$Month, summary)
    aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)

    以前的代码实现了相同的目标和结果,在某些方面,使用的工具只是个人品味和需求的问题;前两个对象在子集方面有非常不同的需求。


    有很多很好的答案可以讨论每个函数的用例差异。答案都没有讨论性能上的差异。这是合理的,因为各种函数期望各种输入并产生各种输出,但是它们中的大多数具有通过系列/组来评估的一般共同目标。我的回答是关注绩效。由于上述原因,矢量的输入创建包含在时序中,因此不测量apply函数。

    我一次测试了两个不同的函数sumlength。测试的体积输入为50M,输出为50K。我还提供了两个目前流行的软件包,这些软件包在提出问题时未被广泛使用,data.tabledplyr。如果你的目标是获得良好的表现,那么两者绝对值得一看。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    library(dplyr)
    library(data.table)
    set.seed(123)
    n = 5e7
    k = 5e5
    x = runif(n)
    grp = sample(k, n, TRUE)

    timing = list()

    # sapply
    timing[["sapply"]] = system.time({
        lt = split(x, grp)
        r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE)
    })

    # lapply
    timing[["lapply"]] = system.time({
        lt = split(x, grp)
        r.lapply = lapply(lt, function(x) list(sum(x), length(x)))
    })

    # tapply
    timing[["tapply"]] = system.time(
        r.tapply <- tapply(x, list(grp), function(x) list(sum(x), length(x)))
    )

    # by
    timing[["by"]] = system.time(
        r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
    )

    # aggregate
    timing[["aggregate"]] = system.time(
        r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
    )

    # dplyr
    timing[["dplyr"]] = system.time({
        df = data_frame(x, grp)
        r.dplyr = summarise(group_by(df, grp), sum(x), n())
    })

    # data.table
    timing[["data.table"]] = system.time({
        dt = setnames(setDT(list(x, grp)), c("x","grp"))
        r.data.table = dt[, .(sum(x), .N), grp]
    })

    # all output size match to group count
    sapply(list(sapply=r.sapply, lapply=r.lapply, tapply=r.tapply, by=r.by, aggregate=r.aggregate, dplyr=r.dplyr, data.table=r.data.table),
           function(x) (if(is.data.frame(x)) nrow else length)(x)==k)
    #    sapply     lapply     tapply         by  aggregate      dplyr data.table
    #      TRUE       TRUE       TRUE       TRUE       TRUE       TRUE       TRUE

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # print timings
    as.data.table(sapply(timing, `[[`,"elapsed"), keep.rownames = TRUE
                  )[,.(fun = V1, elapsed = V2)
                    ][order(-elapsed)]
    #          fun elapsed
    #1:  aggregate 109.139
    #2:         by  25.738
    #3:      dplyr  18.978
    #4:     tapply  17.006
    #5:     lapply  11.524
    #6:     sapply  11.326
    #7: data.table   2.686


    尽管这里有很多好的答案,还有2个基本函数值得一提,有用的outer函数和模糊的eapply函数

    outer是一个非常有用的功能,隐藏为更普通的功能。如果您阅读outer的帮助,其说明如下:

    1
    2
    3
    The outer product of the arrays X and Y is the array A with dimension  
    c(dim(X), dim(Y)) where element A[c(arrayindex.x, arrayindex.y)] =  
    FUN(X[arrayindex.x], Y[arrayindex.y], ...).

    这使得它似乎只对线性代数类型的东西有用。但是,它可以像mapply一样用于将函数应用于两个输入向量。区别在于mapply将函数应用于前两个元素,然后应用于后两个元素,而outer将函数应用于来自第一个向量的一个元素和来自第二个元素的一个元素的每个组合。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     A<-c(1,3,5,7,9)
     B<-c(0,3,6,9,12)

    mapply(FUN=pmax, A, B)

    > mapply(FUN=pmax, A, B)
    [1]  1  3  6  9 12

    outer(A,B, pmax)

     > outer(A,B, pmax)
          [,1] [,2] [,3] [,4] [,5]
     [1,]    1    3    6    9   12
     [2,]    3    3    6    9   12
     [3,]    5    5    6    9   12
     [4,]    7    7    7    9   12
     [5,]    9    9    9    9   12

    当我有一个值向量和一个条件向量时,我亲自使用了这个,并希望看到哪些值符合哪些条件。

    eapply

    eapplylapply类似,不同之处在于它不是将函数应用于列表中的每个元素,而是将函数应用于环境中的每个元素。例如,如果要在全局环境中查找用户定义函数的列表:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    A<-c(1,3,5,7,9)
    B<-c(0,3,6,9,12)
    C<-list(x=1, y=2)
    D<-function(x){x+1}

    > eapply(.GlobalEnv, is.function)
    $A
    [1] FALSE

    $B
    [1] FALSE

    $C
    [1] FALSE

    $D
    [1] TRUE

    坦率地说,我不会使用这个,但如果你正在构建大量的软件包或创建很多环境,它可能会派上用场。


    可能值得一提的是aveavetapply的友好表亲。它以一种可以直接插回数据框的形式返回结果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4))
    means <- tapply(dfr$a, dfr$f, mean)
    ##  A    B    C    D    E
    ## 2.5  6.5 10.5 14.5 18.5

    ## great, but putting it back in the data frame is another line:

    dfr$m <- means[dfr$f]

    dfr$m2 <- ave(dfr$a, dfr$f, FUN=mean) # NB argument name FUN is needed!
    dfr
    ##   a f    m   m2
    ##   1 A  2.5  2.5
    ##   2 A  2.5  2.5
    ##   3 A  2.5  2.5
    ##   4 A  2.5  2.5
    ##   5 B  6.5  6.5
    ##   6 B  6.5  6.5
    ##   7 B  6.5  6.5
    ##   ...

    基本包中没有任何内容像ave一样用于整个数据帧(因为by对于数据帧类似于tapply)。但你可以捏造它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) {
        x <- dfr[x,]
        sum(x$m*x$m2)
    })
    dfr
    ##     a f    m   m2    foo
    ## 1   1 A  2.5  2.5    25
    ## 2   2 A  2.5  2.5    25
    ## 3   3 A  2.5  2.5    25
    ## ...

    我最近发现了相当有用的sweep函数,并在此处添加它是为了完整性:

    基本思想是以行或列方式扫描数组并返回修改后的数组。一个例子将使这一点清楚(来源:datacamp):

    假设您有一个矩阵并希望按列标准化:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    dataPoints <- matrix(4:15, nrow = 4)

    # Find means per column with `apply()`
    dataPoints_means <- apply(dataPoints, 2, mean)

    # Find standard deviation with `apply()`
    dataPoints_sdev <- apply(dataPoints, 2, sd)

    # Center the points
    dataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-")
    print(dataPoints_Trans1)
    ##      [,1] [,2] [,3]
    ## [1,] -1.5 -1.5 -1.5
    ## [2,] -0.5 -0.5 -0.5
    ## [3,]  0.5  0.5  0.5
    ## [4,]  1.5  1.5  1.5
    # Return the result
    dataPoints_Trans1
    ##      [,1] [,2] [,3]
    ## [1,] -1.5 -1.5 -1.5
    ## [2,] -0.5 -0.5 -0.5
    ## [3,]  0.5  0.5  0.5
    ## [4,]  1.5  1.5  1.5
    # Normalize
    dataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev,"/")

    # Return the result
    dataPoints_Trans2
    ##            [,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

    注意:对于这个简单的例子,当然可以通过apply(dataPoints, 2, scale)更容易地实现相同的结果