> v1 <- c(1:8) > v2 <- c(1:8) > v3 <- c(1:8) > v4 <- c(1:8) > v5 <- c(1:8) > v6 <- c(1:8) > v7 <- c(1:8) > v8 <- c(1:8) > v9 <- c(1:8) > v10 <- c(1:8) > v11 <- c(1:8) > v12 <- c(1:8) > expand.grid(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) Error in rep.int(rep.int(seq_len(nx), rep.int(rep.fac, nx)), orep) : invalid 'times' value In addition: Warning message: In rep.int(rep.int(seq_len(nx), rep.int(rep.fac, nx)), orep) : NAs introduced by coercion to integer range
即使有8个向量,它也会杀死我的CPU和/或RAM(> expand.grid(v1,v2,v3,v4,v5,v6,v7,v8)). Here我发现了一些改进,建议使用外部或rep.int.这些解决方案适用于两个向量,因此我无法将其应用于12个向量,但我猜原理是相同的:它创建了驻留在内存中的大型矩阵.我想知道是否有类似python的xrange懒惰评估的东西? Here我发现了delayedAssign函数,但我想这无济于事,因为还有以下内容:
Unfortunately, R evaluates lazy variables when they are pointed to by
a data structure, even if their value is not needed at the time. This
means that infinite data structures, one common application of
laziness in Haskell, are not possible in R.
使用嵌套循环只解决这个问题吗?
PS:我没有具体的问题,但是假设你需要使用接受12个整数参数的函数进行一些计算,出于某种原因.还假设您需要对这12个整数进行所有组合并将结果保存到文件中.使用12个嵌套循环并将结果连续保存到文件将起作用(尽管它会很慢但不会杀死你的RAM). Here显示了如何使用expand.grid和apply函数来替换两个嵌套循环.问题是使用expand.grid创建具有12个长度为8的向量的矩阵具有一些缺点:
>生成这样的矩阵很慢
>如此大的矩阵消耗大量内存(687亿行和8列)
>使用apply进一步迭代此矩阵也很慢
所以在我看来,功能方法比程序解决方案慢得多.我只是想知道是否有可能懒洋洋地创建大型数据结构,理论上它不适合内存并迭代它.就这样.
一种(可以说是更“正确”)的方法是为@BenBolker建议的迭代器编写自己的迭代器(写入扩展的pdf是 here).缺乏更正式的东西,这里是一个穷人的迭代器,类似于expand.grid但是手动推进. (注意:考虑到每次迭代的计算比这个函数本身“更昂贵”,这就足够了.这可以真正改进,但“它有效”.)每次返回返回的函数时,此函数都会返回一个命名列表(带有提供的因子).它是懒惰的,因为它没有扩展整个可能的列表;它本身并不是懒惰,它们应该立即“消耗”.
lazyExpandGrid <- function(...) { dots <- list(...) sizes <- sapply(dots, length, USE.NAMES = FALSE) indices <- c(0, rep(1, length(dots)-1)) function() { indices[1] <<- indices[1] + 1 DONE <- FALSE while (any(rolls <- (indices > sizes))) { if (tail(rolls, n=1)) return(FALSE) indices[rolls] <<- 1 indices[ 1+which(rolls) ] <<- indices[ 1+which(rolls) ] + 1 } mapply(`[`, dots, indices, SIMPLIFY = FALSE) } }
样品用法:
nxt <- lazyExpandGrid(a=1:3, b=15:16, c=21:22) nxt() # a b c # 1 1 15 21 nxt() # a b c # 1 2 15 21 nxt() # a b c # 1 3 15 21 nxt() # a b c # 1 1 16 21 ## <yawn> nxt() # a b c # 1 3 16 22 nxt() # [1] FALSE
注意:为了简洁显示,我使用as.data.frame(mapply(…))作为示例;它可以正常工作,但如果命名列表适合您,则无需转换为data.frame.
编辑
基于alexis_laz’s answer,这是一个大大改进的版本,它(a)更快,(b)允许任意搜索.
lazyExpandGrid <- function(...) { dots <- list(...) argnames <- names(dots) if (is.null(argnames)) argnames <- paste0('Var', seq_along(dots)) sizes <- lengths(dots) indices <- cumprod(c(1L, sizes)) maxcount <- indices[ length(indices) ] i <- 0 function(index) { i <<- if (missing(index)) (i + 1L) else index if (length(i) > 1L) return(do.call(rbind.data.frame, lapply(i, sys.function(0)))) if (i > maxcount || i < 1L) return(FALSE) setNames(Map(`[[`, dots, (i - 1L) %% indices[-1L] %/% indices[-length(indices)] + 1L ), argnames) } }
它不使用任何参数(自动递增内部计数器),一个参数(搜索和设置内部计数器)或向量参数(寻找每个参数并将计数器设置为最后一个,返回data.frame).
最后一个用例允许对设计空间的子集进行采样:
set.seed(42) nxt <- lazyExpandGrid2(a=1:1e2, b=1:1e2, c=1:1e2, d=1:1e2, e=1:1e2, f=1:1e2) as.data.frame(nxt()) # a b c d e f # 1 1 1 1 1 1 1 nxt(sample(1e2^6, size=7)) # a b c d e f # 2 69 61 7 7 49 92 # 21 72 28 55 40 62 29 # 3 88 32 53 46 18 65 # 4 88 33 31 89 66 74 # 5 57 75 31 93 70 66 # 6 100 86 79 42 78 46 # 7 55 41 25 73 47 94
感谢alexis_laz对cumprod,Map和索引计算的改进!