我有foreach
循环,在每个循环中生成一个列表,.combine
函数将它们组合起来,如下所示:
mergelists = function(x,xn) {
padlen = length(x[[1]])
for (n in names(x)[!names(x) %in% names(xn)]) xn[[n]] = 0
for (n in names(xn)[!names(xn) %in% names(x)]) xn[[n]] = c(rep(0,padlen), xn[[n]])
for (idx in names(xn)) { x[[idx]] = c( x[[idx]], xn[[idx]] ) }
x
}
前两个for循环修改新列表(xn
)以使其与收集结果的那个(x
)兼容。最后一个将x
和xn
加入x
。
我相信我的代码效率低得离谱,因为它会重新分配很多并使用for循环。但我无法考虑更好的解决方案。有什么想法吗?
更多解释: 我事先并不知道列表名称(它们是来自foreach部分的引导练习的模式)。
示例:
> x
$foo
[1] 3 2
$bar
[1] 3 2
和
> xn
$foo
[1] 1
$baz
[1] 1
应加入
> x
$foo
[1] 3 2 1
$bar
[1] 3 2 0
$baz
[1] 0 0 1
就是这样。
答案 0 :(得分:3)
如果foo和bar存在于每个列表中且按顺序排列,则mapply可以正常工作。正如@BenBarnes建议的那样,通过预处理步骤来创建0,即使它们不存在于任何地方,也是一个可行的选择。排序很容易。我已经将0改为NAs,因为这似乎更合适。
# Make data
x <- list(foo=c(3,2),bar=c(6,7))
xn <- list(foo=c(1),bar=c(1),aught=c(5,2))
lol <- list(x=x,xn=xn)
# Pre-process
allnames <- sort(unique(unlist(lapply(lol, names))))
cleanlist <- function(l,allnames) {
ret <- l[allnames]
names(ret) <- allnames
ret[sapply(ret,is.null)] <- NA
ret
}
lol <- lapply(lol,cleanlist,allnames=allnames)
# Combine
do.call("mapply", c(c,lol) )
产生:
aught bar foo
x NA 6 3
xn1 5 7 2
xn2 2 1 1
<强>基准强>
那就是说,如果你希望速度提升,原版本仍然是最快的,大概是因为它做得最少。但是无环路方法非常优雅,可以扩展到任意数量的x。
library(microbenchmark)
microbenchmark( mergelists(lol$x,lol$xn), mergeList2(lol$x,lol$xn), do.call("mapply", c(c,lol) ) )
Unit: microseconds
expr min lq median uq max
1 do.call("mapply", c(c, lol)) 155.048 159.5175 192.0635 195.5555 245.841
2 mergeList2(lol$x, lol$xn) 19938.288 20095.9905 20225.4750 20719.6730 27143.674
3 mergelists(lol$x, lol$xn) 63.416 68.1650 78.0825 84.3680 95.265
答案 1 :(得分:3)
在我的基准测试中,这种方法比你的方法花费的时间更长,但是因为我已经解决了,所以我认为无论如何我都会发布它。这是双倍的努力。如果名称完全未知,并且您被迫在.combine
函数中使用零填充,则可以尝试以下操作。 (也许首先在迭代的子集上尝试它以查看它是否有效):
library(reshape2)
mergeList2 <- function(x, xn) {
xDF <- data.frame(ID = seq_along(x[[1]]), x)
xnDF <- data.frame(ID = seq_along(xn[[1]]) + nrow(xDF), xn)
meltedX <- melt(xDF, id = "ID")
meltedXN <- melt(xnDF, id = "ID")
res <- as.list(dcast(rbind(meltedX, meltedXN), ID ~ variable,
fill = 0))[-1]
return(res)
}
你的例子:
mergeList2(list(foo = c(3, 2), bar = c(3, 2)),
list(foo = 1, baz= 1))
# $foo
# [1] 3 2 1
# $bar
# [1] 3 2 0
# $baz
# [1] 0 0 1
使用foreach
示例
set.seed(1)
foreach(dd = 1:10, .combine = mergeList2) %do% {
theNames <- sample(c("foo", "bar", "baz"), 2)
ans <- as.list(rpois(2, 4))
names(ans) <- theNames
ans
}
# $foo
# [1] 4 7 2 4 0 2 0 4 5 3
# $baz
# [1] 7 0 0 5 3 5 3 4 0 5
# $bar
# [1] 0 5 2 0 5 0 0 0 6 0