4

我有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。最后一个加入xxn进入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

就是这样。

4

2 回答 2

3

如果 foo 和 bar 存在于每个列表中并且按顺序排列,则 mapply 有效。正如@BenBarnes 所建议的那样,有一个预处理步骤来创建 0 使得这是一个可行的选择,即使它们并不存在于任何地方。排序很容易。我已将 0 更改为 NA,因为这似乎更合适。

# 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

在此处输入图像描述

于 2012-04-19T14:15:46.223 回答
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
于 2012-04-19T15:44:57.703 回答