首页 > 解决方案 > 如何避免为大型数据集编写嵌套的 for 循环?

问题描述

对于一个二变量问题,outer这很可能是最好的解决方案,如果循环的空间足够小,那么我们就可以expand.grid做我们的工作了。但是,如果我们有两个以上的变量和很大的循环空间,则这些都被排除在外。outer不能处理两个以上的变量,并且expand.grid消耗的内存比我见过的机器能够占用的还要多。

我最近发现自己在编写这样的代码:

n<-1000
for(c in 1:n){
    for(b in 1:c){
        for(a in 1:b){
            if(foo(a,b,c))
            {
                bar(a,b,c)
            }
        }
    }
}

在这些情况下,嵌套循环似乎是自然的解决方案(例如mapply,不会这样做,也没有好的因素tapply可供使用),但有更好的方法吗?这似乎是通往糟糕代码的道路。

我怀疑它combn可能会以某种方式做到这一点,但根据我的经验,它很快就会陷入与expand.grid. 如果记忆有用,我也知道它采取了不明智的步骤,告诉我更改我的递归限制的全局设置。

标签: rnested-loops

解决方案


这是与重复的组合。可能是您开箱即用的最佳工具,但在n = 1000L5 亿个组合中,这将占用约 2GB 的内存。

library(RcppAlgos)
n = 1000L
mat <- comboGeneral(n, 3L, repetition = TRUE)

现在有两条路要走。如果你有 RAM 并且你的函数能够被矢量化,你可以非常快速地完成上述操作。假设如果组合的总和大于 1000,您需要组合的均值,否则您需要组合的总和。

res <- if (rowSums(mat) > 1000L) 
  rowMeans(mat)
else
  rowSums(mat)

## Error: cannot allocate vector of size 1.2 Gb

不好了!我得到了可怕的分配向量错误。允许您返回函数的结果。但请注意,它返回一个列表并且速度要慢得多,因为它必须评估您的 R 函数而不是停留在 C++ 中。正因为如此,我改成了n = 100L因为我没有整天...

comboGeneral(100L, 3L, repetition = TRUE,
                        FUN = function(x) { 
                          if (sum(x) > 100L)
                            mean(x)
                          else
                            sum(x)
                        }
)

如果我有一个静态集合,我总是从 中选择 3 个组合n,我可能会Rcpp直接使用代码,具体取决于什么foo(a,b,c)bar(a,b,c)是,但首先我想了解更多关于函数的信息。


推荐阅读