【问题标题】:Convert for loops into foreach loops将 for 循环转换为 foreach 循环
【发布时间】:2024-06-22 01:35:01
【问题描述】:

我想通过使用 foreach 包使下面的代码更高效。我尝试了很长时间,但没有得到与使用 for 循环时相同的结果。我想使用一个嵌套的 foreach 循环,包括并行化......作为输出,我想有两个带有暗淡 [R,b1] 的矩阵,我将非常感谢一些建议!!

n  <- c(100, 300, 500)
R  <- 100
b0 <- 110
b1 <- seq(0.01, 0.1, length.out = 100)

## all combinations of n and b1
grid <- expand.grid(n, b1)
names(grid) <- c("n", "b1")

calcPower <- function( R, b0, grid) {
  
  cl <- makeCluster(3)
  registerDoParallel(cl)
  
  ## n and b1 coefficients
  n  <- grid$n
  b1 <- grid$b1
  
  ## ensures reproducibility
  set.seed(2020)
  x      <- runif(n, 18, 80)
  x.dich <- factor( ifelse( x < median( x), 0, 1))
  
  ## enables to store two outputs
  solution <- list()
  
  ## .options.RNG ensures reproducibility
  res <- foreach(i = 1:R, .combine = rbind, .inorder = TRUE, .options.RNG = 666) %dorng% { 
    p.val   <- list()
    p.val.d <- list()
  
    for( j in seq_along(b1)) {
      
      y <- b0 + b1[j] * x + rnorm(n, 0, sd = 10)
      
      mod.lm   <- lm( y ~ x)
      mod.lm.d <- lm( y ~ x.dich)
      
      p.val    <- c( p.val,   ifelse( summary(mod.lm)$coef[2,4]   <= 0.05, 1, 0))
      p.val.d  <- c( p.val.d, ifelse( summary(mod.lm.d)$coef[2,4] <= 0.05, 1, 0))
    }
    
    solution[[1]] <- p.val
    solution[[2]] <- p.val.d
    
    return(solution)
    }
  
  dp.val   <- matrix( unlist(res[,1], use.names = FALSE), R, length(b1), byrow = TRUE)
  dp.val.d <- matrix( unlist(res[,2], use.names = FALSE), R, length(b1), byrow = TRUE)
  
  stopCluster(cl)

  df <- data.frame(
    effectS = b1,
    power   = apply( dp.val,   2, function(x){ mean(x) * 100}),
    power.d = apply( dp.val.d, 2, function(x){ mean(x) * 100}),
    n = factor(n))
  
  return(df)
}

## simulation for different n
tmp <- with(grid,
            by( grid, n,
               calcPower, R = R, b0 = b0))

## combines the 3 results
df.power  <- rbind(tmp[[1]], tmp[[2]], tmp[[3]])

【问题讨论】:

    标签: r foreach doparallel


    【解决方案1】:

    我在以下代码中创建了一个foreach 循环。必须做出一些改变。在foreach 中返回一个列表比返回一个矩阵要容易得多,因为它与rbind 结合在一起。特别是当您想返回多个时。我的解决方案是将所有内容保存在一个列表中,然后将其转换为长度为 100 的矩阵。

    注意:您的代码中有一个错误。 summary( mod.lm.d)$coef[2,4] 不存在。我将其更改为 [2]。根据您的需要进行调整

    solution <- list()
    df2<-foreach(i = 1:R, .combine = rbind, .inorder=TRUE) %dopar%{
      set.seed(i)
      p.val <- list()
      p.val.d <- list()
      counter <- list()
      for( j in seq_along(b1)){
        
        x      <- sort( runif(n, 18, 80))
        x.dich <- factor( ifelse( x < median(x), 0, 1))
        y      <- b0 + b1[j] * x + rnorm( n, 0, sd = 10)
        
        mod.lm   <- lm( y ~ x)
        mod.lm.d <- lm( y ~ x.dich)
        
        p.val    <- c(p.val, ifelse( summary( mod.lm)$coef[2] <= 0.05, 1, 0))
        p.val.d  <- c(p.val.d, ifelse( summary( mod.lm.d)$coef[2] <= 0.05, 1, 0))
        counter <- c(counter, j)
      }
      solution[[1]] <- p.val
      solution[[2]] <- p.val.d
      solution[[3]] <- counter
      return(solution)
    }
    
    dp.val <- unlist(df2[,1], use.names = FALSE)
    dp.val.d <-  unlist(df2[,2], use.names = FALSE)
    
    dp.val.matr <- matrix(dp.val, R, length(b1))
    dp.val.d.matr <- matrix(dp.val.d, R, length(b1))
    
    stopCluster(cl)
    

    您的评论:

    foreach 确实适用于正常的 for 循环。最小可重现示例:

    df<-foreach(i = 1:R, .combine = cbind, .inorder=TRUE) %dopar%{
      x <- list()
      for(j in 1:3){
        x <- c(x,j)
      }
      return(x)
    }
    

    【讨论】:

    • 现在我意识到这不是我想象的那样。在 foreach 循环中不能使用普通的 for 循环。 j 始终 = 1 并且不会遍历 1:length(b1)。只是让你知道。很抱歉再次抱怨!
    • @Andrea a foreach 确实适用于 for 循环。我在答案中插入了一个例子。另外我会检查我的解决方案,不太确定是否出了问题。也许我在加入数据时犯了一个错误。
    • @Andrea 我想我在处理你的变量时犯了一个错误。我将代码更改为更节省的方式来处理您的问题。我还给你插入了一个计数器变量,你可以用df2[,3] 调用它。您可以看到 j 确实进行了迭代。但我建议删除它。这只是不必要的时间损失。