使用 doparallel 在 foreach 循环内循环

Posted

技术标签:

【中文标题】使用 doparallel 在 foreach 循环内循环【英文标题】:loop inside a foreach loop using doparallel 【发布时间】:2017-10-19 20:40:58 【问题描述】:

我有一个包含循环的函数

myfun = function(z1.d, r, rs)
  x = z1.d[,r]
  or.d = order(as.vector(x), decreasing=TRUE)[rs]
  zz1.d = as.vector(x)
  r.l = zz1.d[or.d]

  y=vector()
  for (i in 1:9)
  
    if(i<9) y[i]=mean( x[(x[,r] >= r.l[i] & x[,r] < r.l[i+1]),r] ) else
      y[i] =  mean( z1.d[(x >= r.l[9]),r] )
  
  return(y)

rs 是数值向量,z1.d 是动物园,y 也是数值向量。

当我尝试在并行循环中运行函数时:

cls = makePSOCKcluster(8)
registerDoParallel(cls)

rlarger.d.1  = foreach(r=1:dim(z1.d)[2], .combine = "cbind") %dopar%     
  myfun(z1.d, r, rs)

stopCluster(cls)

我收到以下错误:

Error in  : task 1 failed - "incorrect number of dimensions"

我不知道为什么,但我意识到如果我将循环从我的函数中取出,它不会给出错误。

另外,如果我使用 %do% 而不是 %dopar% 运行完全相同的代码(所以不是并行运行),它工作正常(缓慢但没有错误)。

编辑:这里要求的是参数示例:

dim(z1.d)
[1] 8766  107
> z1.d[1:4,1:6]
                    AU_10092 AU_10622 AU_12038 AU_12046 AU_13017 AU_14015
1966-01-01 23:00:00       NA       NA       NA    1.816        0    4.573
1966-01-02 23:00:00       NA       NA       NA    9.614        0    4.064
1966-01-03 23:00:00        0       NA       NA    0.000        0    0.000
1966-01-04 23:00:00        0       NA       NA    0.000        0    0.000

> rs
[1] 300 250 200 150 100  75  50  30  10

r 在 foreach 循环中定义

【问题讨论】:

参数 z1.d, rs, r 的示例输入会很有帮助。 @sbg - 你运行的是什么操作系统。在并行执行的上下文中,这一点很重要。由于 Windows、Linux 和 MacOS 在某些情况下通过 R 公开了不同的并行实现。 我在windows中运行它 我对@9​​87654325@ 并不完全熟悉,但通常,在使用并行内核时,变量需要“发送”到内核环境。在您的情况下,我看不到您在核心环境中声明 z1.drs 的位置。正如我所说,我真的不知道foreach,但我会使用它:rlarger.d.1 = foreach(r=1:dim(z1.d)[2], z1.d = z1.d, rs = rs, .combine = "cbind") %dopar% myfun(z1.d, r, rs)。顺便说一句,通常像这里的r这样的函数的可变参数应该首先在你的参数函数myfun = function(r, z1.d, rs)中定义。 @你是在windows上还是在其他操作系统上? 【参考方案1】:

弹出错误是因为您未能在您的工作人员上启动zoo。因此,工作人员不知道如何正确处理动物园对象,而是将它们作为矩阵处理,这些矩阵在子集化时的行为方式不同! 因此,快速解决您所说的问题的方法是将.packages="zoo" 添加到您的foreach 电话中。

在我看来,您甚至不需要进行并行计算。如果您使用数字向量而不是动物园对象,您可以显着增强您的功能:

# sample time series to match your object's size
set.seed(1234)
z.test <- as.zoo(replicate(107,sample(c(NA,runif(1000,0,10)),size = 8766, replace = TRUE)))

myfun_new <-  function(z, r, rs)
  x <-  as.numeric(z[,r])
  r.l <- x[order(x, decreasing=TRUE)[rs]]
  res_dim <- length(rs)
  y=numeric(res_dim)
  for (i in 1:res_dim)
    if(i< res_dim) 
      y[i] <- mean( x[(x >= r.l[i] & x < r.l[i+1])], na.rm = TRUE ) 
    else
      y[i] <-   mean( x[(x >= r.l[res_dim])] , na.rm = TRUE)
    
  
  return(y)

简单的计时显示了改进:

system.time(
  cls = makePSOCKcluster(4)
  registerDoParallel(cls)
  rlarger.d.1 = foreach(r=1:dim(z.test)[2],.packages = "zoo", .combine = "cbind") %dopar%  
    myfun(z.test, r, rs)
  stopCluster(cls)
)
##  User      System verstrichen 
##  0.08        0.10       10.93
system.time(
  res <-sapply(1:dim(z.test)[2], function(r)myfun_new(z.test, r, rs))
)
##  User      System verstrichen 
##  0.48        0.21        0.68

虽然结果相同(只是列名不同)

all.equal(res, rlarger.d.1, check.attributes = FALSE)
## [1] TRUE

【讨论】:

谢谢!您的建议是一种更有效的方法!【参考方案2】:

好像你的函数代码有错误。

在第 2 行中,您创建了一个一维对象

x = z1.d[,r]

在第 9 行中,您将其视为二维 1

x[some_logic, r]

这就是您出现“维度数不正确”错误的原因。虽然,我不知道为什么它在 %do% 变体中有效。

在任何情况下,您都需要将 for 循环内的代码替换为:

if(i<9) y[i]=mean( x[(x[,r] >= r.l[i] & x[,r] < r.l[i+1])] ) else
      y[i] =  mean( x[(x >= r.l[9])] )

或与:

if(i<9) y[i]=mean( z1.d[(x[,r] >= r.l[i] & x[,r] < r.l[i+1]),r] ) else
      y[i] =  mean( z1.d[(x >= r.l[9]),r] )

由于您没有提供可重现的示例,因此我没有对其进行测试。

【讨论】:

以上是关于使用 doparallel 在 foreach 循环内循环的主要内容,如果未能解决你的问题,请参考以下文章

Foreach和doparallel而不是R中的for循环

有没有办法在 xts 中将 period.apply 与 doParallel 和 foreach 一起使用?

foreach、doParallel 和随机生成

在 R doParallel foreach 循环中运行 ovun.sample

在 R 中使用 doParallel 的 foreach 时,Windows Defender 的 CPU 使用率非常高

使用 foreach 函数和 doParallel 库在 R 中嵌套 for 循环