8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

分组函数(tapply、by、aggregate)和 *apply 系列

Gereon will not feed AI 1月前

84 0

每当我想在 R 中执行 \'map\'py 操作时,我通常会尝试使用 apply 系列中的函数。但是,我从未完全理解它们之间的区别 -- 如何 {sapply、lapply 等}...

每当我想在 R 中执行某些\'map\'py 操作时,我通常会尝试使用该 apply 系列中的函数。

然而,我从来没有完全理解它们之间的区别——{ sapply , lapply 等} 如何将函数应用于输入/分组输入,输出会是什么样子,甚至输入可以是什么——所以我经常只是仔细检查它们直到得到我想要的。

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

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

  1. p5

  2. p6

  3. apply(matrix, 1/2, f) :输入是一个矩阵。输出是一个向量,其中元素 i 是 f(矩阵的第 i 行/第 i 列)
  4. tapply(vector, grouping, f) :输出是一个矩阵/数组,其中矩阵/数组中的元素是 f 向量分组 g ,并 g 被推送到行/列名称
  5. by(dataframe, grouping, f) :设为 g 分组。应用于 f 组/数据框的每一列。漂亮地打印分组和 f 每列的值。
  6. aggregate(matrix, grouping, f) :类似于 by ,但聚合不会漂亮地打印输出,而是将所有内容粘贴到数据框中。

附带问题:我还没有学过 plyr 或 reshape - 是否会 plyr 完全 reshape 取代这些?

帖子版权声明 1、本帖标题:分组函数(tapply、by、aggregate)和 *apply 系列
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Gereon will not feed AI在本站《list》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 助记符:l 代表“列表”,s 代表“简化”,t 代表“每种类型”(分组的每个级别都是一个类型)

  • Rfast 包中还存在一些函数,例如:eachcol.apply、apply.condition 等,它们比 R 的等效函数更快

  • R 有许多 *apply 函数,这些函数在帮助文件中有详细描述(例如 ?apply )。不过,这些函数数量足够多,初学者可能难以决定哪一个适合他们的情况,甚至无法记住所有函数。他们可能有一种普遍的感觉,“我应该在这里使用 *apply 函数”,但一开始要记住所有函数可能很难。

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

    这个答案旨在为新用户提供某种 指引 ,帮助他们找到适合特定问题的正确 *apply 函数。请注意,这并不是 简单 地重复或替换 R 文档!希望这个答案能帮助您决定哪个 *apply 函数适合您的情况,然后由您进一步研究它。除了一个例外,性能差异将不会得到解决。

    • p3

       # 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

      p4

    • p5

      p6

       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
    • p7

      p8

       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 

      p9

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

      p10

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

      p11

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

      p12

    • p13

      p14

       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
    • p15

      p16

       #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
    • p17

       Map(sum, 1:5, 1:5, 1:5) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 9 [[4]] [1] 12 [[5]] [1] 15
    • 第18页

      第19页

       # 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")
    • 第20页

      第21页

      第22页

       x <- 1:20

      第23页

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

      第24页

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

      第25页

  • 相信你会发现,by 是纯粹的 split-lapply,而聚合是 tapply。我认为黑羊可以制作出极好的面料。

  • 很棒的回复!这应该是官方 R 文档的一部分 :)。一个小建议:也许可以添加一些关于使用聚合和 by 的要点?(听了你的描述,我终于明白了!但它们很常见,所以将这两个函数分开并提供一些具体示例可能会很有用。)

  • 附注:以下是各种 plyr 功能与基本 *apply 功能的对应关系(来自 plyr 网页 http://had.co.nz/plyr/ )

    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 功能更困难。

    plyr reshape 函数几乎取代了我日常使用中的所有函数。但是,从 Plyr 简介文档中也可以看出:

    相关函数 tapply sweep 在中没有对应函数 plyr ,并且仍然有用。 merge 对于将摘要与原始数据相结合很有用。

  • 当我从头开始学习 R 时,我发现 plyr 比 *apply() 系列函数容易学习得多。对我来说,ddply() 非常直观,因为我熟悉 SQL 聚合函数。ddply() 成为我解决许多问题的利器,其中一些问题可以用其他命令更好地解决。

  • 第 21 张幻灯片 http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :

    apply, sapply, lapply, by, aggregate

    (希望可以清楚地看到 apply 与@Hadley 的相对应 aaply ,并 aggregate 与@Hadley 的 ddply 等相对应。如果您从这张图片中看不清楚,同一张幻灯片的第 20 张幻灯片将会让您看得更清楚。)

    (左边是输入,上面是输出)

  • 乔兰 (Joran) 的精彩回答 开始 —— 怀疑有什么可以比这更好。

    那么以下助记符可能有助于记住它们之间的区别。虽然有些是显而易见的,但其他的可能不那么明显——对于这些,你可以在 Joran 的讨论中找到依据。

    助记符

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

    建立正确的背景

    如果使用 apply 家庭对你来说仍然感觉有点陌生,那么可能是因为你缺少了一个关键的观点。

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

    Lisp 的用户会立即认出这个范式。如果你不熟悉 Lisp,一旦你理解了 FP,你就会获得一个在 R 中使用的强大观点——而且 apply 会更有意义。

  • 因为我意识到这篇文章的(非常出色的)答案缺乏 by 解释 aggregate 。这是我的贡献。

    经过

    然而,正如文档中所述, by tapply 当我们想要计算 by 无法处理的 tapply , 的威力

    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 
    

    如果我们打印这两个对象 ct cb ,我们“本质上”会得到相同的结果,唯一的区别在于它们的显示方式和不同的 class 属性, by 分别 cb array ct .

    正如我所说, by 当我们无法使用时,的力量就会显现出来 tapply ;下面的代码就是一个例子:

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

    R 表示参数必须具有相同的长度,比如“我们想要计算 summary 因子 iris 中所有变量的 Species ”:但是 R 无法做到这一点,因为它不知道如何处理。

    使用 by 函数 R 为类分派一个特定的方法 data frame ,然后让该 summary 函数工作,即使第一个参数的长度(以及类型)不同。

    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

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

     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 可以看作是另一种不同的使用方式

    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 是一些示例(可在文档中获取):

    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 ,但语法稍微复杂一些,并且输出(在某些情况下)可读性较差:

    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
    

    还有一些时候我们不能使用 by tapply ,而必须使用 aggregate .

     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 ):

    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

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

    其他时候,结果相同,差异仅在于类(以及它如何显示/打印,而不仅仅是——例如,如何对其进行子集)对象:

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

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

  • 有很多很棒的答案讨论了每个函数用例的差异。没有一个答案讨论性能差异。这是合理的,因为不同的函数需要不同的输入并产生不同的输出,但它们中的大多数都有一个共同的目标,即按系列/组进行评估。我的答案将重点关注性能。由于上述原因,从向量创建的输入包含在计时中,因此函数也 apply 没有被测量。

    我一次测试了两个不同的函数 sum length 。测试的输入量为 50M,输出量为 50K。我还包含了两个目前流行的软件包,在提出问题时它们还没有被广泛使用, data.table dplyr 。如果您追求良好的性能,这两个软件包绝对值得一看。

    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 
    

    # 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
    
  • 尽管这里有很多很棒的答案,但还有两个基本函数值得一提,即有用的 outer 函数和模糊的 eapply 函数

    outer 是一个非常有用的功能,但却隐藏在普通功能中。如果你阅读帮助,你 outer 会发现它的描述是这样的:

    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 将函数应用于第一个向量中的一个元素和第二个向量中的一个元素的每个组合。例如:

     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 类似于 lapply ,只不过它不是将函数应用于列表中的每个元素,而是将函数应用于环境中的每个元素。例如,如果您想在全局环境中查找用户定义函数的列表:

    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 
    

    坦率地说,我并不经常使用它,但如果您正在构建很多包或创建很多环境,它可能会派上用场。

  • 引用 14

    也许值得一提的 ave . ave tapply 它的友好表亲。它以您可以直接插入数据框的形式返回结果。

    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 那样

    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 ):

    假设您有一个矩阵并想 按列对其 进行标准化

    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,"-")
    
    # 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)

  • @Frank:好吧,老实说,这篇文章的标题相当具有误导性:当你阅读问题本身时,你会发现它是关于“apply 系列”的。sweep 是一个高阶函数,就像这里提到的所有其他函数一样,例如 apply、sapply、lapply。因此,可以针对获得 1,000 多个赞同的已接受答案及其中给出的示例提出同样的问题。只需查看那里给出的 apply 示例即可。

  • 引用 17

    sweep 的名称、默认值和参数名称都具有误导性 :)。我这样理解更容易:1) STATS 是向量或单个值,将重复形成与第一个输入大小相同的矩阵,2) FUN 将应用于第一个输入和这个新矩阵。也许更好的例子是:sweep(matrix(1:6,nrow=2),2,7:9,list)。它通常比 apply 更有效,因为在 apply 循环中,sweep 能够使用矢量化函数。

  • collapse 包中,我尝试将大多数常见的应用功能压缩为仅 2 个函数:

    1. dapply (Data-Apply) 将函数应用于矩阵和数据框的行或(默认)列,并(默认)返​​回相同类型和相同属性的对象(除非每个计算的结果都是原子的和 drop = TRUE )。性能与数据框列相当 lapply ,比 apply 矩阵行或列快约 2 倍。可通过以下方式实现并行性 mclapply (仅适用于 MAC)。

    句法:

    dapply(X, FUN, ..., MARGIN = 2, parallel = FALSE, mc.cores = 1L, 
           return = c("same", "matrix", "data.frame"), drop = TRUE)
    

    例子:

    # Apply to columns:
    dapply(mtcars, log)
    dapply(mtcars, sum)
    dapply(mtcars, quantile)
    # Apply to rows:
    dapply(mtcars, sum, MARGIN = 1)
    dapply(mtcars, quantile, MARGIN = 1)
    # Return as matrix:
    dapply(mtcars, quantile, return = "matrix")
    dapply(mtcars, quantile, MARGIN = 1, return = "matrix")
    # Same for matrices ...
    
    1. BY 是使用向量、矩阵和 data.frame 方法进行拆分-应用-合并计算的 S3 通用方法。它比 tapply , by aggregate (也比快 plyr ,但在大数据上 dplyr 更快)。

    句法:

    BY(X, g, FUN, ..., use.g.names = TRUE, sort = TRUE,
       expand.wide = FALSE, parallel = FALSE, mc.cores = 1L,
       return = c("same", "matrix", "data.frame", "list"))
    

    例子:

    # Vectors:
    BY(iris$Sepal.Length, iris$Species, sum)
    BY(iris$Sepal.Length, iris$Species, quantile)
    BY(iris$Sepal.Length, iris$Species, quantile, expand.wide = TRUE) # This returns a matrix 
    # Data.frames
    BY(iris[-5], iris$Species, sum)
    BY(iris[-5], iris$Species, quantile)
    BY(iris[-5], iris$Species, quantile, expand.wide = TRUE) # This returns a wider data.frame
    BY(iris[-5], iris$Species, quantile, return = "matrix") # This returns a matrix
    # Same for matrices ...
    

    分组变量列表也可以提供给 g .

    谈到性能: collapse 是促进 R 中的高性能编程,并完全超越 split-apply-combine。为此,该包有一整套基于 C++ 的快速通用函数: fmean , fmedian , fmode , fsum , fprod , fsd , fvar , fmin , fmax , ffirst , flast , fNobs , fNdistinct , fscale , fbetween , fwithin , fHDbetween , fHDwithin , flag , fdiff fgrowth 。它们在一次数据传递中执行分组计算(即无需拆分和重新组合)。

    句法:

    fFUN(x, g = NULL, [w = NULL,] TRA = NULL, [na.rm = TRUE,] use.g.names = TRUE, drop = TRUE)
    

    例子:

    v <- iris$Sepal.Length
    f <- iris$Species
    
    # Vectors
    fmean(v)             # mean
    fmean(v, f)          # grouped mean
    fsd(v, f)            # grouped standard deviation
    fsd(v, f, TRA = "/") # grouped scaling
    fscale(v, f)         # grouped standardizing (scaling and centering)
    fwithin(v, f)        # grouped demeaning
    
    w <- abs(rnorm(nrow(iris)))
    fmean(v, w = w)      # Weighted mean
    fmean(v, f, w)       # Weighted grouped mean
    fsd(v, f, w)         # Weighted grouped standard-deviation
    fsd(v, f, w, "/")    # Weighted grouped scaling
    fscale(v, f, w)      # Weighted grouped standardizing
    fwithin(v, f, w)     # Weighted grouped demeaning
    
    # Same using data.frames...
    fmean(iris[-5], f)                # grouped mean
    fscale(iris[-5], f)               # grouped standardizing
    fwithin(iris[-5], f)              # grouped demeaning
    
    # Same with matrices ...
    

    dplyr 进行编程要快得多 data.table ,尤其是在处理较小的数据时,但在处理大数据时也一样。

  • 引用 19

    从 R 4.3.0 开始, tapply 将支持数据框和两者, tapply by 支持使用公式对数据框行进行分组。

    > R.version.string
    [1] "R version 4.3.0 beta (2023-04-07 r84200)"
    > dd <- data.frame(x = 1:10, f = gl(5L, 2L), g = gl(2L, 5L))
        x f g
    1   1 1 1
    2   2 1 1
    3   3 2 1
    4   4 2 1
    5   5 3 1
    6   6 3 2
    7   7 4 2
    8   8 4 2
    9   9 5 2
    10 10 5 2
    > tapply(dd, ~f + g, nrow)
       g
    f   1 2
      1 2 0
      2 2 0
      3 1 1
      4 0 2
      5 0 2
    > by(dd, ~g, identity)
    g: 1
      x f g
    1 1 1 1
    2 2 1 1
    3 3 2 1
    4 4 2 1
    5 5 3 1
    ------------------------------------------------------------ 
    g: 2
        x f g
    6   6 3 2
    7   7 4 2
    8   8 4 2
    9   9 5 2
    10 10 5 2
    
  • 引用 20

    一些软件包中也存在一些替代方案,上面没有讨论过。

    中的函数 parApply() parallels 在集群上执行并行计算提供了 apply 系列函数的替代方案。R 中并行计算的其他替代方案包括包 foreach doParallel 包,它们允许并行执行循环和函数。 future 包提供了一个简单而一致的 API 来使用 Future,这是一种异步评估表达式的方法,可以并行或顺序进行。此外,包还提供 purrr 了一种函数式编程方法来进行迭代和映射,并通过 future 包支持并行化。

    以下是一些示例

    parApply() 示例:

    library(parallel)
    
    # Create a matrix
    m <- matrix(1:20, nrow = 5)
    
    # Define a function to apply to each column of the matrix
    my_fun <- function(x) {
      x^2
    }
    
    # Apply the function to each column of the matrix in parallel
    result <- parApply(cl = makeCluster(2), X = m, MARGIN = 2, FUN = my_fun)
    
    # View the result
    result
    

    foreach 示例:

    library(foreach)
    library(doParallel)
    
    # Register a parallel backend
    registerDoParallel(cores = 2)
    
    # Create a list of numbers
    my_list <- list(1, 2, 3, 4, 5)
    
    # Define a function to apply to each element of the list
    my_fun <- function(x) {
      x^2
    }
    
    # Apply the function to each element of the list in parallel
    result <- foreach(i = my_list) %dopar% my_fun(i)
    
    # View the result
    result
    

    未来的例子:

    library(future)
    
    # Plan to use a parallel backend
    plan(multisession, workers = 2)
    
    # Create a list of numbers
    my_list <- list(1, 2, 3, 4, 5)
    
    # Define a function to apply to each element of the list
    my_fun <- function(x) {
      x^2
    }
    
    # Apply the function to each element of the list in parallel using futures
    result <- future_map(my_list, my_fun)
    
    # View the result
    result
    

    purrr示例:

    library(purrr)
    library(future)
    
    # Plan to use a parallel backend
    plan(multisession, workers = 2)
    
    # Create a list of numbers
    my_list <- list(1, 2, 3, 4, 5)
    
    # Define a function to apply to each element of the list
    my_fun <- function(x) {
      x^2
    }
    
    # Apply the function to each element of the list in parallel using purrr
    result <- future_map(my_list, my_fun)
    
    # View the result
    result
    

    编辑于 2023-07-02( 未来 作者):用以下代码替换已弃用且不再存在的 multiprocess 未来 后端: multisession .

返回
作者最近主题: