每当我想在 R 中执行 \'map\'py 操作时,我通常会尝试使用 apply 系列中的函数。但是,我从未完全理解它们之间的区别 -- 如何 {sapply、lapply 等}...
每当我想在 R 中执行某些\'map\'py 操作时,我通常会尝试使用该 apply
系列中的函数。
然而,我从来没有完全理解它们之间的区别——{ sapply
, lapply
等} 如何将函数应用于输入/分组输入,输出会是什么样子,甚至输入可以是什么——所以我经常只是仔细检查它们直到得到我想要的。
有人可以解释一下何时如何使用哪一个吗?
我目前的理解(可能不正确/不完整)是......
p5
p6
apply(matrix, 1/2, f)
:输入是一个矩阵。输出是一个向量,其中元素 i
是 f(矩阵的第 i 行/第 i 列)
tapply(vector, grouping, f)
:输出是一个矩阵/数组,其中矩阵/数组中的元素是 f
向量分组 g
,并 g
被推送到行/列名称
by(dataframe, grouping, f)
:设为 g
分组。应用于 f
组/数据框的每一列。漂亮地打印分组和 f
每列的值。
aggregate(matrix, grouping, f)
:类似于 by
,但聚合不会漂亮地打印输出,而是将所有内容粘贴到数据框中。
附带问题:我还没有学过 plyr 或 reshape - 是否会 plyr
完全 reshape
取代这些?
有很多很棒的答案讨论了每个函数用例的差异。没有一个答案讨论性能差异。这是合理的,因为不同的函数需要不同的输入并产生不同的输出,但它们中的大多数都有一个共同的目标,即按系列/组进行评估。我的答案将重点关注性能。由于上述原因,从向量创建的输入包含在计时中,因此函数也 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