如何让foreach将返回值存入三维数组(R语言)
生成指定形状三维数组的解决方案
我需要执行K次一个返回numVals × N矩阵的函数,最终要将所有结果存储为形状c(numVals, N, K)的双精度多维数组。目前用foreach的.combine = rbind会得到二维矩阵,事后重塑担心内存开销,希望要么让foreach直接输出三维数组,要么用无额外内存开销的方式重塑。
原始代码如下:
library(doParallel) library(doRNG) registerDoParallel(cores = 3) registerDoRNG(12345) run_tasks <- function(k, N, numVals) { return(matrix(runif(numVals * N), numVals, N)) } K <- 10000 N <- 40 numVals <- 10 # 当前写法得到二维矩阵 results <- foreach(k = 1:K, .combine = rbind) %dorng% run_tasks(k, N, numVals) # 期望的输出格式 # results <- array(NA, c(numVals, N, K))
方案1:自定义合并函数让foreach直接生成三维数组
可以借助数组合并工具或自定义逻辑,让foreach每次迭代的结果直接沿第三维拼接,最终得到目标形状的数组。
方法1:使用abind包(简洁高效)
library(doParallel) library(doRNG) library(abind) # 加载数组合并工具包 registerDoParallel(cores = 3) registerDoRNG(12345) run_tasks <- function(k, N, numVals) { matrix(runif(numVals * N), numVals, N) } K <- 10000 N <- 40 numVals <- 10 # 用abind作为合并函数,指定沿第三维拼接 results <- foreach(k = 1:K, .combine = abind, .combine.args = list(along = 3)) %dorng% { run_tasks(k, N, numVals) } # 验证维度:输出应为 numVals, N, K dim(results)
方法2:自定义合并函数(无需额外包)
如果不想依赖第三方包,可以自己实现合并逻辑,处理初始矩阵到三维数组的转换,后续每次迭代沿第三维追加:
library(doParallel) library(doRNG) registerDoParallel(cores = 3) registerDoRNG(12345) run_tasks <- function(k, N, numVals) { matrix(runif(numVals * N), numVals, N) } # 自定义三维数组合并函数 combine_3d <- function(a, b) { if (is.null(a)) { # 第一次迭代,将矩阵转为三维数组 array(b, dim = c(dim(b), 1)) } else { # 扩展第三维并追加新结果 new_dim <- c(dim(a)[1], dim(a)[2], dim(a)[3] + 1) a <- array(a, dim = new_dim) a[,,new_dim[3]] <- b a } } K <- 10000 N <- 40 numVals <- 10 # 初始化设为NULL,使用自定义合并函数 results <- foreach(k = 1:K, .combine = combine_3d, .init = NULL) %dorng% { run_tasks(k, N, numVals) } dim(results)
方案2:低内存开销的原地重塑
如果坚持用.combine = rbind得到二维矩阵,可以通过修改维度属性和置换维度的方式实现几乎无额外内存开销的重塑:
library(doParallel) library(doRNG) registerDoParallel(cores = 3) registerDoRNG(12345) run_tasks <- function(k, N, numVals) { matrix(runif(numVals * N), numVals, N) } K <- 10000 N <- 40 numVals <- 10 # 先得到rbind后的二维矩阵 results <- foreach(k = 1:K, .combine = rbind) %dorng% run_tasks(k, N, numVals) # 第一步:原地修改维度为(numVals, K, N) dim(results) <- c(numVals, K, N) # 第二步:置换维度顺序为(numVals, N, K) results <- aperm(results, c(1, 3, 2)) dim(results)
说明:
- 修改
dim属性是原地操作,不会复制数据,仅调整对象的维度元信息; aperm函数用于调整维度顺序,它不会复制底层数据,仅修改维度的访问顺序,因此几乎没有额外内存开销。
内容的提问来源于stack exchange,提问作者titfortat
相关产品推荐
相关产品推荐

