分组函数(t应用、by、聚合)和*应用系列

每当我想在R中做一些“map”py的事情时,我通常会尝试使用apply系列中的函数。

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

谁能解释一下什么时候用哪个?

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

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

  2. lapply(vec, f):与sapply相同,但输出是列表?

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

附带问题:我还没有学习plyr或重塑——plyrreshape会完全取代所有这些吗?

431818 次浏览

附带说明,这里是各种plyr函数如何对应于基本*apply函数(来自plyr网页http://had.co.nz/plyr/的plyr简介文档)

Base function   Input   Output   plyr function---------------------------------------aggregate        d       d       ddply + colwiseapply            a       a/l     aaply / alplyby               d       l       dlplylapply           l       l       llplymapply           a       a/l     maply / mlplyreplicate        r       a/l     raply / rlplysapply           l       a       laply

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

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

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

相关函数tapplysweepplyr中没有对应的函数,并且仍然有用。merge用于将摘要与原始数据组合起来。

R中有许多在帮助文件(例如?apply)中很好地描述的*应用函数。但是,它们已经足够多了,刚开始使用的useR可能很难决定哪一个适合他们的情况,甚至很难记住它们。他们可能有一种普遍的感觉,“我应该在这里使用一个*应用函数”,但一开始很难把它们都弄清楚。

尽管事实上(在其他答案中注意到)*应用系列的大部分功能都包含在非常流行的plyr包中,但基本功能仍然有用且值得了解。

此答案旨在作为新用户的一种路标,以帮助将他们引导到针对其特定问题的正确*应用函数。请注意,这是没有旨在简单地反演或替换R留档!希望此答案可以帮助您决定哪个*应用函数适合您的情况,然后由您进一步研究它。除了一个例外,性能差异将不会被解决。

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

     # Two dimensional matrixM <- matrix(seq(1,16), 4, 4)
    # apply min to rowsapply(M, 1, min)[1] 1 2 3 4
    # apply max to columnsapply(M, 2, max)[1]  4  8 12 16
    # 3 dimensional arrayM <- array( seq(32), dim = c(4,4,2))
    # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimensionapply(M, 1, sum)# Result is one-dimensional[1] 120 128 136 144
    # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimensionapply(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

    如果您想要二维矩阵的行/列均值或总和,请务必研究高度优化、闪电般快速的colMeansrowMeanscolSumsrowSums.

  • 我申请-当您想将函数应用于依次列出并获取列表。

    这是许多其他*应用函数的主力。剥离返回他们的代码,你经常会在下面找到lapply

     x <- list(a = 1, b = 1:3, c = 10:100)lapply(x, FUN = length)$a[1] 1$b[1] 3$c[1] 91lapply(x, FUN = sum)$a[1] 1$b[1] 6$c[1] 5005
  • 应用-当您想将函数应用于列表,但你想要一个向量回来,而不是一个列表。

    如果您发现自己正在输入unlist(lapply(...)),请停下来考虑一下sapply.

     x <- list(a = 1, b = 1:3, c = 10:100)# Compare with above; a named vector, not a listsapply(x, FUN = length)a  b  c1  3 91
    sapply(x, FUN = sum)a    b    c1    6 5005

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

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

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

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

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

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

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

  • 瓦普利-当您想使用sapply但可能需要从代码中挤出更多的速度或想要更多的类型安全

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

     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  c1  3 91
  • 穆普利-当您有几个数据结构(例如:向量,列表)并且您想将函数应用于第一个元素然后是每个元素的第二个元素,等等,强制结果sapply中的向量/数组。

    这是多变量的,因为你的函数必须接受多个参数。

     #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
  • 地图-#0和#1的包装器,因此保证返回列表。

     Map(sum, 1:5, 1:5, 1:5)[[1]][1] 3
    [[2]][1] 6
    [[3]][1] 9
    [[4]][1] 12
    [[5]][1] 15
  • 快速-当您想要递归地将函数应用于嵌套列表结构的每个元素时。

    为了让你知道rapply有多不常见,我在第一次发布这个答案时忘记了它!显然,我相信很多人都使用它,但是YMMV。rapply最好用用户定义的函数来说明:

     # Append ! to string, otherwise incrementmyFun <- function(x){if(is.character(x)){return(paste(x,"!",sep=""))}else{return(x + 1)}}
    #A nested list structurel <- 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 characterrapply(l, myFun)
    # Result is a nested list like l, with values alteredrapply(l, myFun, how="replace")
  • 适用-当您想将函数应用于向量和子集由其他向量定义,通常是系数。

    *应用家族的害群之马,某种程度上。帮助文件的使用短语“破烂的数组”可能有点令人困惑,但它实际上是很简单

    一个向量:

     x <- 1:20

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

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

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

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

    可以在定义子组的地方处理更复杂的示例由几个因素的列表的唯一组合。tapply是在精神上类似于拆分-应用-组合函数常见于R(aggregatebyaveddply等),因此害群之马

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

聚合

(希望很明显,apply对应于@Hadley的aaplyaggregate对应于@Hadley的ddply等。

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

首先从Joran的精彩回答开始--怀疑任何事情都可以更好。

然后下面的记忆法可能有助于记住两者之间的区别。虽然有些是显而易见的,但其他的可能不那么明显-因为这些你会在Joran的讨论中找到理由。

记忆法

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

建立正确的背景

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

这两篇文章会有所帮助,它们提供了必要的背景知识来激励apply系列函数所提供的函数式编程技术

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

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

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## ...

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

BY

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

ct <- tapply(iris$Sepal.Width , iris$Species , summary )cb <- by(iris$Sepal.Width , iris$Species , summary )
cbiris$Species: setosaMin. 1st Qu.  Median    Mean 3rd Qu.    Max.2.300   3.200   3.400   3.428   3.675   4.400--------------------------------------------------------------iris$Species: versicolorMin. 1st Qu.  Median    Mean 3rd Qu.    Max.2.000   2.525   2.800   2.770   3.000   3.400--------------------------------------------------------------iris$Species: virginicaMin. 1st Qu.  Median    Mean 3rd Qu.    Max.2.200   2.800   3.000   2.974   3.175   3.800

ct$setosaMin. 1st Qu.  Median    Mean 3rd Qu.    Max.2.300   3.200   3.400   3.428   3.675   4.400
$versicolorMin. 1st Qu.  Median    Mean 3rd Qu.    Max.2.000   2.525   2.800   2.770   3.000   3.400
$virginicaMin. 1st Qu.  Median    Mean 3rd Qu.    Max.2.200   2.800   3.000   2.974   3.175   3.800

如果我们打印这两个对象,ctcb,我们“本质上”有相同的结果,唯一的区别是它们的显示方式和不同的class属性,分别是cbbyctarray

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

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

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

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

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

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

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

 by(iris, iris$Species, mean)iris$Species: setosa[1] NA-------------------------------------------iris$Species: versicolor[1] NA-------------------------------------------iris$Species: virginica[1] NAWarning messages:1: In mean.default(data[x, , drop = FALSE], ...) :argument is not numeric or logical: returning NA2: In mean.default(data[x, , drop = FALSE], ...) :argument is not numeric or logical: returning NA3: 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)
atsetosa versicolor  virginica5.006      5.936      6.588agGroup.1     x1     setosa 5.0062 versicolor 5.9363  virginica 6.588

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

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

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

ag <- aggregate(len ~ ., data = ToothGrowth, mean)
agsupp dose   len1   OJ  0.5 13.232   VC  0.5  7.983   OJ  1.0 22.704   VC  1.0 16.775   OJ  2.0 26.066   VC  2.0 26.14

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

att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)
attOJ    VC0.5 13.23  7.981   22.70 16.772   26.06 26.14

有时我们不能使用bytapply,我们必须使用aggregate

 ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)
ag1Month    Ozone     Temp1     5 23.61538 66.730772     6 29.44444 78.222223     7 59.11538 83.884624     8 59.96154 83.961545     9 31.44828 76.89655

我们无法在一次调用中获得tapply的前一个结果,但我们必须计算每个元素的Month平均值,然后组合它们(还要注意我们必须调用na.rm = TRUE,因为aggregate函数的formula方法默认具有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      ta25 23.61538 65.548396 29.44444 79.100007 59.11538 83.903238 59.96154 83.967749 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函数也没有测量。

我一次测试了两个不同的函数sumlength。测试的体积50M输入,50K输出。我还包括了两个当前流行的软件包,它们在提出问题时没有广泛使用,data.tabledplyr。如果你的目标是良好的性能,这两个软件包绝对值得一看。

library(dplyr)library(data.table)set.seed(123)n = 5e7k = 5e5x = runif(n)grp = sample(k, n, TRUE)
timing = list()
# sapplytiming[["sapply"]] = system.time({lt = split(x, grp)r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE)})
# lapplytiming[["lapply"]] = system.time({lt = split(x, grp)r.lapply = lapply(lt, function(x) list(sum(x), length(x)))})
# tapplytiming[["tapply"]] = system.time(r.tapply <- tapply(x, list(grp), function(x) list(sum(x), length(x))))
# bytiming[["by"]] = system.time(r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE))
# aggregatetiming[["aggregate"]] = system.time(r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE))
# dplyrtiming[["dplyr"]] = system.time({df = data_frame(x, grp)r.dplyr = summarise(group_by(df, grp), sum(x), n())})
# data.tabletiming[["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 countsapply(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 timingsas.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 dimensionc(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

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

我最近发现了相当有用的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 pointsdataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-")
# Return the resultdataPoints_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
# NormalizedataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev, "/")
# Return the resultdataPoints_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)更容易地实现

在最近在CRAN上发布的崩溃包中,我尝试将大多数常见的应用功能压缩为两个函数:

  1. dapply(数据应用)将函数应用于矩阵和data.frames的行或(默认)列,并(默认)返回具有相同类型和相同属性的对象(除非每次计算的结果是原子和drop = TRUE)。data.frame列的性能与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是一个S3泛型,用于使用向量、矩阵和data.frame方法进行拆分-应用-组合计算。它比tapplybyaggregate快得多(也比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.framesBY(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.frameBY(iris[-5], iris$Species, quantile, return = "matrix") # This returns a matrix# Same for matrices ...

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

谈论性能:fmode1的一个主要目标是在R语言中培养高性能编程,并超越拆分-应用-合并的所有功能。为此,该包有一整套基于C++的快速泛型函数:fmeanfmedianfmodefsumfprodfsdfvarfminfmaxffirstfmedian0、fmedian1、fmedian2、fmedian3、fmedian4、fmedian5、fmedian6、fmedian7、fmedian8、fmedian9和fmode0。它们在一次传递数据中执行分组计算(即不分裂和重组)。

语法:

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

示例:

v <- iris$Sepal.Lengthf <- iris$Species
# Vectorsfmean(v)             # meanfmean(v, f)          # grouped meanfsd(v, f)            # grouped standard deviationfsd(v, f, TRA = "/") # grouped scalingfscale(v, f)         # grouped standardizing (scaling and centering)fwithin(v, f)        # grouped demeaning
w <- abs(rnorm(nrow(iris)))fmean(v, w = w)      # Weighted meanfmean(v, f, w)       # Weighted grouped meanfsd(v, f, w)         # Weighted grouped standard-deviationfsd(v, f, w, "/")    # Weighted grouped scalingfscale(v, f, w)      # Weighted grouped standardizingfwithin(v, f, w)     # Weighted grouped demeaning
# Same using data.frames...fmean(iris[-5], f)                # grouped meanfscale(iris[-5], f)               # grouped standardizingfwithin(iris[-5], f)              # grouped demeaning
# Same with matrices ...

在包小插曲中,我提供了基准。使用快速函数编程比使用dplyrdata.table编程要快得多,尤其是在较小的数据上,也在大数据上。