我有一个相同长度向量的列表,我需要对列表中的每个向量元素运行不同的统计函数。我知道我可以通过首先创建一个数据框然后按行运行函数来使用
apply
来做到这一点:
apply(X = do.call(what = "data.frame", args = foo), MARGIN = 1, FUN = "sum", na.rm = TRUE)
当运行几十万次时,这个过程非常慢。 通过
Reduce
我设法得到了部分解决方案,例如当使用 +
而不是 sum
时,速度要快得多:
Reduce(f = "+", x = foo)
不过,我没有设法获得纯
Reduce
版本,可以使用 mean
、sd
等函数以及其他参数,以及关于参数 na.rm = TRUE
。
在依赖
apply
的同时加快速度的次优解决方案仍然只是使用 Reduce
加速矩阵创建:
apply(X = Reduce(function(...) cbind(...), foo), MARGIN = 1, FUN = "sum", na.rm = TRUE)
这是可重现的示例以及我迄今为止提出的解决方案的比较。
foo <- lapply(X = 1:1e2, FUN = function(x) 1:10)
microbenchmark::microbenchmark("apply_do.call" = apply(X = do.call(what = "data.frame", args = foo), MARGIN = 1, FUN = "sum", na.rm = TRUE),
"apply_Reduce" = apply(X = Reduce(f = function(...) cbind(...), x = foo), MARGIN = 1, FUN = "sum", na.rm = TRUE),
"Reduce" = Reduce(f = "+", x = foo))
这会在我的机器(Linux)上产生以下输出
Unit: microseconds
expr min lq mean median uq max neval cld
apply_do.call 4256.337 4406.775 5211.12032 4603.8670 5180.1275 13508.14 100 c
apply_Reduce 292.775 326.124 480.88223 346.5525 436.1935 7559.97 100 b
Reduce 37.505 43.197 51.53856 46.7935 55.2790 131.72 100 a
如您所见,与第一个示例相比,
Reduce
版本仅需要大约 1% 的计算时间,计算改进的潜力是巨大的。
有没有一种方法可以在不依赖外部库的情况下有效地解决我的问题(与最小值、最大值、总和、平均值、标准差...兼容)?如果答案能够正确计算
mean
和 sd
等统计数据,并保留 POSIXct
等对象类型,即可获得加分。
apply
是为矩阵设计的,将其应用在data.frames上效率非常低。尝试使用 cbind
代替,或者使用 matrixStats::rowSums2
等 cpp 函数。
> microbenchmark::microbenchmark(
+ apply_df=apply(do.call("data.frame", foo), 1, sum, na.rm=TRUE),
+ apply_mat=apply(do.call("cbind", foo), 1, sum, na.rm=TRUE),
+ ms=matrixStats::rowSums2(do.call(what="cbind", args=foo)),
+ check='equal')
Unit: microseconds
expr min lq mean median uq max neval cld
apply_df 9192.141 9475.6885 9975.73942 9702.244 9989.6995 18370.500 100 a
apply_mat 93.222 98.1140 104.11094 101.521 107.2095 145.461 100 b
ms 39.455 42.2835 49.48344 48.328 56.0690 66.346 100 b