有效地修改R中的列表

时间:2012-04-19 12:04:15

标签: r for-loop

我有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

就是这样。

2 个答案:

答案 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

enter image description here

答案 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