如何在R中的“ foreach”循环中转换“ for循环”?

时间:2019-04-04 02:02:22

标签: r foreach parallel-processing

我要解决的问题是需要并行计算才能获得比传统的“ for循环”更快的结果。

问题出在这里

我需要为列表对象内数据框中包含的198135个结果变量生成线性模型。我必须将模型中每个预测变量的所有beta和p值以及它们的拟合优度存储在数据库中。

我编写了一个可以正常完成任务的功能性“ for循环”,但完成该过程需要35个小时以上。我知道R使用的我的8核CPU不到20%,我想全部使用。问题是我不知道如何利用并行计算在foreach循环中转换for循环。

以下是我的问题的一些示例代码:

library(tidyverse)
library(broom)

## Example data 

outcome_list <- list(as.data.frame(cbind(rnorm(32), dataframe_id = c(1))),
                     as.data.frame(cbind(rnorm(32), dataframe_id =  c(2))),
                     as.data.frame(cbind(rnorm(32), dataframe_id =  c(3)))) ## This represents my list of 198135 dataframes

mtcars <- mtcars #I will use the explanatory variables from here



## Below this line is my current solution with a for loop that works fine

x <- list()
results_df <- as.data.frame(cbind(dataframe_id = c(0), intercept = c(0),
                                b_mpg = c(0), p_mpg = c(0),
                                b_cyl = c(0), p_cyl = c(0),
                                p.model = c(0), AIC = c(0),
                                BIC = c(0)))

for(i in 1:3){
  x[[i]] <- lm(outcome_list[[i]]$V1 ~ mtcars$mpg + mtcars$cyl)
  gof <- broom::glance(x[[i]])
  betas <- broom::tidy(x[[i]])
  results_df <- rbind(results_df, c(outcome_list[[i]]$V2[1], 
                                    betas$estimate[1],
                                    betas$estimate[2], betas$p.value[2], 
                                    betas$estimate[3], betas$p.value[3],
                                    gof$p.value, gof$r.squared, gof$AIC,
                                    gof$BIC))

  if(i %% i == 0){
    message(paste(i, "of 3")) # To know if my machine has not crashed
    x <- list() # To keep RAM clean of useless data
  }
  gc()
}

results_df <- results_df[-1, ]



使用上面显示的代码,我得到了所需的结果(一个具有回归参数的数据框,并且列表中的每个结果变量都拟合良好),但是它很慢,因为我无法使用所有电脑功能。

我知道使用“ foreach”和“ doParallel”软件包可以更快地解决此问题,但是我仍然不了解foreach循环结构背后的逻辑,因为这是我第一次需要处理这么多的循环数据。

PS:我已经尝试过几种foreach函数的方法,但是我什么都没得到。我没有写foreach解决方案的空缺,因为我不了解自己在做什么。

1 个答案:

答案 0 :(得分:0)

您可以这样做:

## Example data 
outcome_list <- list(as.data.frame(cbind(rnorm(32), dataframe_id = c(1))),
                     as.data.frame(cbind(rnorm(32), dataframe_id = c(2))),
                     as.data.frame(cbind(rnorm(32), dataframe_id = c(3))))

## Parallel code
library(doParallel)
registerDoParallel(cl <- makeCluster(3))
results_list <- foreach(i = 1:3) %dopar% {

  mylm <- lm(outcome_list[[i]]$V1 ~ mtcars$mpg + mtcars$cyl)
  gof <- broom::glance(mylm)
  betas <- broom::tidy(mylm)

  c(outcome_list[[i]]$V2[1], 
    betas$estimate[1],
    betas$estimate[2], betas$p.value[2], 
    betas$estimate[3], betas$p.value[3],
    gof$p.value, gof$r.squared, gof$AIC,
    gof$BIC)
}
stopCluster(cl)

results_df <- setNames(as.data.frame(do.call("rbind", results_list)),
                       c("dataframe_id", "intercept", "b_mpg", "p_mpg", 
                         "b_disp", "p_disp", "p.model", "AIC", "BIC"))

您将结果返回到foreach(类似于lapply)中,而不是增长对象(在并行BTW中是不可能的)。

详细了解如何使用foreach there