【问题标题】:Parallelize nested for-loop on 3 dimensional array in R在R中的3维数组上并行嵌套for循环
【发布时间】:2016-11-26 01:06:30
【问题描述】:

在 Windows 机器上使用 R,我目前正在 3D 数组 (720x360x1368) 上运行一个嵌套循环,该循环遍历 d1 和 d2 以在 d3 上应用一个函数并将输出组装到一个类似维度的新数组中。

在以下可重现的示例中,我将尺寸减少了 10 倍,以加快执行速度。

library(SPEI)

old.array = array(abs(rnorm(50)), dim=c(72,36,136))

new.array = array(dim=c(72,36,136))

for (i in 1:72) {
  for (j in 1:36) {
    new.listoflists <- spi(ts(old.array[i,j,], freq=12, start=c(1901,1)), 1, na.rm = T)
    new.array[i,j,] = new.listoflists$fitted
  }
}

其中 spi() 是 SPEI 包中的一个函数,它返回一个列表列表,其中一个特定的列表 $fitted 长度为 1368,用于从每个循环增量中构造新数组。

虽然这个循环完美无缺,但它需要相当长的时间来计算。我读过foreach可用于并行化for循环。

但是,我不明白如何实现新数组的嵌套和组装,以使新旧数组的名称一致。

(最后,我希望能够使用as.data.frame.table() 将旧数组和新数组都转换为“平面”长面板数据框,并将它们沿它们的三个维度合并。 )

对于如何使用并行计算实现所需输出的任何帮助,我们将不胜感激!

干杯
立方汤姆

【问题讨论】:

  • 我真的不知道你想做什么 (reproducible example!),但你是否考虑过使用在 n 维数组中通用的apply()apply() 的第二个参数定义了应用函数的维度,例如apply( old.array , 1:2 , FUN = function(x) paste0(x) ) 以在数组的第三维上应用 paste0() 函数为例。
  • 谢谢西蒙,我听从了你的建议,让我的例子可以重现。
  • 我也尝试过使用 apply:new2.array &lt;- apply(old.array , 1:2 , FUN = function(x) spi(ts(x, freq=12, start=c(1901,1)), 1, na.rm = T) ),它非常快,但只给了我一个包含 72*36=2592 个元素的矩阵,每个元素都是 8 个元素的列表。我只对这 8 个中的第二个 ($fitted),它是与 d3 上的每个条目对应的所需函数值的列表。
  • 如果我理解正确,那么您可以这样做:new2.array &lt;- apply(old.array , 1:2 , FUN = function(x) spi(ts(x, freq=12, start=c(1901,1)), 1, na.rm = T)$fitted )
  • 这解决了嵌套列表问题!但是,尺寸尚未按正确的顺序排列。将所有内容包装在 aperm 中会有所帮助,这样结果与我的循环示例相同。使用 tictoc 测量执行时间在这个解决方案上给了我 36.51 秒,而在我的循环可执行示例上为 39.88 秒。将实际数据中的每个维度乘以 10,这将是一个显着的改进(尽管诚然没有我希望的那么大)。谢谢西蒙!

标签: arrays r multidimensional-array foreach parallel-processing


【解决方案1】:

我没有使用与您的问题一样多的维度,因为我想确保行为是正确的。 所以在这里我使用了带有多个参数的 mapply 。结果是结果列表。然后我用 matrix() 包裹它以获得您希望的尺寸。 请注意,i 使用 times 重复,j 使用 each 重复。这很关键,因为 matrix() 首先按行放置条目,然后在达到行数时换行到下一列。

new.array = array(1:(5*10*4), dim=c(5,10,4))

# FUN: function which returns lists of 
FUN <- function(x){
    list(lapply(x, rep, times=3))
}

# result of the computation
result <- matrix(
    mapply(
        function(i,j,...){

            FUN(new.array[i,j,])
        }
        ,i = rep(1:nrow(new.array),times=ncol(new.array))
        ,j = rep(1:ncol(new.array),each=nrow(new.array))
        ,new.array=new.array
    )
    ,nrow=nrow(new.array)
    ,ncol=ncol(new.array)
)

【讨论】:

  • 感谢您的回复!我尝试采用您的代码,但一小时后停止执行,没有任何结果:new3.array = array(dim=c(72,36,136)) # result of the computation new3.array &lt;- matrix( mapply( function(i,j,...){ spi(ts(old.array[i,j,], freq=12, start=c(1901,1)), 1, na.rm = T) } ,i = rep(1:nrow(old.array),times=ncol(old.array)) ,j = rep(1:ncol(old.array),each=nrow(old.array)) ,old.array=old.array ) ,nrow=nrow(old.array) ,ncol=ncol(old.array) )我在这里遗漏了什么吗?
【解决方案2】:

如果有一个可重现的例子会更好,这是我想出的:

首先创建要使用的集群

cl <- makeCluster(6, type = "SOCK")
registerDoSNOW(cl)

然后你创建循环并关闭集群:

zz <- foreach(i = 1:720, .combine = c) %:% 
foreach(j = 1:360, .combine = c ) %dopar% {
new.listoflists <- FUN(old.array[i,j,])
new.array[i,j,] <- new.listoflists$list
}
stopCluster(cl)

这将创建一个包含 new.array[i,j,] 的每个迭代的列表 zz,然后您可以将它们绑定在一起:

new.obj <- plyr::ldply(zz, data.frame)

希望对您有所帮助!

【讨论】:

  • 谢谢,tia_0!这对我理解如何在我的情况下并行化 for 循环有很大帮助!因此,我将此标记为正确答案。对于感兴趣的读者:@Simon 在对我最初问题的评论中的回答也很有魅力。
猜你喜欢
  • 1970-01-01
  • 2021-09-03
  • 1970-01-01
  • 1970-01-01
  • 2021-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-02
相关资源
最近更新 更多