do.call 不适用于“+”作为 "what" 和 3 个以上元素的列表
do.call doesn't work with "+" as "what" and a list of 3+ elements
我可以使用 do.call
对两个向量进行元素求和:
do.call(what="+", args =list(c(0,0,1), c(1,2,3))
>[1] 1 2 4
但是,如果我想用三个向量的列表调用同一个运算符,它会失败:
do.call(what = "+", args = list(c(0,0,1), c(1,2,3), c(9,1,2)))
>Error in `+`(c(0, 0, 1), c(1, 2, 3), c(9, 1, 2)): operator needs one or two arguments
我可以用Reduce
Reduce(f = "+", x = list(c(0,0,1), c(1,2,3), c(9,1,2)))
>[1] 10 3 6
但我知道与 do.call
相比,Reduce
操作产生的开销,并且在我的真实应用程序中这是不能容忍的,因为我不需要对 3 元素列表求和,而是 10^4 元素长向量的 10^5 元素列表。
UPD:Reduce
原来是最快的方法,毕竟...
lst <- list(1:10000, 10001:20000, 20001:30000)
lst2 <- lst[rep(seq.int(length(lst)), 1000)]
microbenchmark::microbenchmark(colSums(do.call(rbind, lst2)),
vapply(transpose(lst2), sum, 0),
Reduce(f = "+", x = lst2))
Unit: milliseconds
expr min lq mean median uq max neval cld
colSums(do.call(rbind, lst2)) 153.5086 194.9139 222.7954 198.1952 201.8152 915.6354 100 b
vapply(transpose(lst2), sum, 0) 398.9424 537.3834 732.4747 781.7255 813.7376 1538.4301 100 c
Reduce(f = "+", x = lst2) 101.5618 105.5864 139.8651 108.1204 112.7861 2567.1793 100 a
您可以使用:
colSums(do.call(rbind, lst))
#[1] 10 3 6
或类似的:
rowSums(do.call(cbind, lst))
其中 lst
是:
lst <- list(c(0,0,1), c(1,2,3), c(9, 1, 2))
随着您的列表变大,您可能会发现这开始变快:
# careful if you use the tidyverse that purrr does not mask transpose
library(data.table)
lst <- list(c(0,0,1), c(1,2,3), c(9, 1, 2))
vapply(transpose(lst), sum, 0)
# [1] 10 3 6
我拿了几个答案来比较速度,好像是你想要的。
# make the list a bit bigger...
lst2 <- lst[rep(seq.int(length(lst)), 1000)]
microbenchmark::microbenchmark(Reduce(`+`, lst2),
colSums(do.call(rbind, lst2)),
vapply(transpose(lst2), sum, 0),
eval(str2lang(paste0(lst2,collapse = "+"))))
)
Unit: microseconds
expr min lq mean median uq max neval
Reduce(`+`, lst2) 954.9 1088.10 1341.271 1191.05 1389.00 6923.2 100
colSums(do.call(rbind, lst2)) 402.2 474.80 761.473 538.85 843.75 7079.7 100
vapply(transpose(lst2), sum, 0) 81.9 91.85 110.455 103.90 119.00 330.4 100
eval(str2lang(paste0(lst2, collapse = "+"))) 17489.2 18466.65 20767.888 19572.25 20809.80 57770.4 100
虽然这里有更长的向量,但您的用例也是如此。此基准测试需要一两分钟才能完成 运行。请注意单位现在以毫秒为单位。我认为这将取决于列表的长度。
lst <- list(1:10000, 10001:20000, 20001:30000)
lst2 <- lst[rep(seq.int(length(lst)), 1000)]
microbenchmark::microbenchmark(colSums(do.call(rbind, lst2)),
vapply(transpose(lst2), sum, 0))
)
Unit: milliseconds
expr min lq mean median uq max neval
colSums(do.call(rbind, lst2)) 141.7147 146.6305 188.5108 163.4915 228.7852 270.5679 100
vapply(transpose(lst2), sum, 0) 261.8630 335.6093 348.6241 341.6958 348.6404 495.0994 100
另一个基本 R 解决方法
rowSums(as.data.frame(lst)
或
eval(str2lang(paste0(lst,collapse = "+")))
这给出了
[1] 10 3 6
数据
lst <- list(c(0,0,1), c(1,2,3), c(9, 1, 2))
我可以使用 do.call
对两个向量进行元素求和:
do.call(what="+", args =list(c(0,0,1), c(1,2,3))
>[1] 1 2 4
但是,如果我想用三个向量的列表调用同一个运算符,它会失败:
do.call(what = "+", args = list(c(0,0,1), c(1,2,3), c(9,1,2)))
>Error in `+`(c(0, 0, 1), c(1, 2, 3), c(9, 1, 2)): operator needs one or two arguments
我可以用Reduce
Reduce(f = "+", x = list(c(0,0,1), c(1,2,3), c(9,1,2)))
>[1] 10 3 6
但我知道与 do.call
相比,Reduce
操作产生的开销,并且在我的真实应用程序中这是不能容忍的,因为我不需要对 3 元素列表求和,而是 10^4 元素长向量的 10^5 元素列表。
UPD:Reduce
原来是最快的方法,毕竟...
lst <- list(1:10000, 10001:20000, 20001:30000)
lst2 <- lst[rep(seq.int(length(lst)), 1000)]
microbenchmark::microbenchmark(colSums(do.call(rbind, lst2)),
vapply(transpose(lst2), sum, 0),
Reduce(f = "+", x = lst2))
Unit: milliseconds
expr min lq mean median uq max neval cld
colSums(do.call(rbind, lst2)) 153.5086 194.9139 222.7954 198.1952 201.8152 915.6354 100 b
vapply(transpose(lst2), sum, 0) 398.9424 537.3834 732.4747 781.7255 813.7376 1538.4301 100 c
Reduce(f = "+", x = lst2) 101.5618 105.5864 139.8651 108.1204 112.7861 2567.1793 100 a
您可以使用:
colSums(do.call(rbind, lst))
#[1] 10 3 6
或类似的:
rowSums(do.call(cbind, lst))
其中 lst
是:
lst <- list(c(0,0,1), c(1,2,3), c(9, 1, 2))
随着您的列表变大,您可能会发现这开始变快:
# careful if you use the tidyverse that purrr does not mask transpose
library(data.table)
lst <- list(c(0,0,1), c(1,2,3), c(9, 1, 2))
vapply(transpose(lst), sum, 0)
# [1] 10 3 6
我拿了几个答案来比较速度,好像是你想要的。
# make the list a bit bigger...
lst2 <- lst[rep(seq.int(length(lst)), 1000)]
microbenchmark::microbenchmark(Reduce(`+`, lst2),
colSums(do.call(rbind, lst2)),
vapply(transpose(lst2), sum, 0),
eval(str2lang(paste0(lst2,collapse = "+"))))
)
Unit: microseconds
expr min lq mean median uq max neval
Reduce(`+`, lst2) 954.9 1088.10 1341.271 1191.05 1389.00 6923.2 100
colSums(do.call(rbind, lst2)) 402.2 474.80 761.473 538.85 843.75 7079.7 100
vapply(transpose(lst2), sum, 0) 81.9 91.85 110.455 103.90 119.00 330.4 100
eval(str2lang(paste0(lst2, collapse = "+"))) 17489.2 18466.65 20767.888 19572.25 20809.80 57770.4 100
虽然这里有更长的向量,但您的用例也是如此。此基准测试需要一两分钟才能完成 运行。请注意单位现在以毫秒为单位。我认为这将取决于列表的长度。
lst <- list(1:10000, 10001:20000, 20001:30000)
lst2 <- lst[rep(seq.int(length(lst)), 1000)]
microbenchmark::microbenchmark(colSums(do.call(rbind, lst2)),
vapply(transpose(lst2), sum, 0))
)
Unit: milliseconds
expr min lq mean median uq max neval
colSums(do.call(rbind, lst2)) 141.7147 146.6305 188.5108 163.4915 228.7852 270.5679 100
vapply(transpose(lst2), sum, 0) 261.8630 335.6093 348.6241 341.6958 348.6404 495.0994 100
另一个基本 R 解决方法
rowSums(as.data.frame(lst)
或
eval(str2lang(paste0(lst,collapse = "+")))
这给出了
[1] 10 3 6
数据
lst <- list(c(0,0,1), c(1,2,3), c(9, 1, 2))