R中的并行计算,用于通过循环保存数据

Posted

技术标签:

【中文标题】R中的并行计算,用于通过循环保存数据【英文标题】:Parallel computation in R for saving data over loops 【发布时间】:2020-04-08 17:45:06 【问题描述】:

我在以下简单代码上应用并行以在多个循环中使用 Openxlsx 保存输出的努力失败了。

任何人都可以帮助将此代码转换为并行模式。此代码基于实际大小的数据(超过 5000 万次观察,需要 13 小时才能运行)。减少 2 小时对我来说是一项艰巨的工作。

library(dplyr)
library(readxl)
library(openxlsx)
library(foreach)
library(doParallel)


rawdata <- readxl::read_xlsx("~/Desktop/Book1.xlsx")


TYPE1 <- rawdata %>% filter(TYPE == "A") 
TYPE2 <- rawdata %>% filter(TYPE == "B") 

Split.TYPE1 <- split(TYPE1, TYPE1$Name) 
Split.TYPE2 <- split(TYPE2, TYPE2$Name) 


#--------------------------------- Save the TYPE A reports------------------------------------------------------------------------------
###################################(the foreach lines are coded)
for (nm in names(Split.TYPE1))
#foreach(nm=1:names(Split.TYPE1), .combine=cbind) %dopar% 
  file<-paste0(nm,".xlsx")
  d1<-as.data.frame(Split.TYPE1[[nm]])

  wb<-createWorkbook(file)
  addWorksheet(wb, "test", gridLines = T)
  writeData(wb, sheet = "test", x = d1)
  saveWorkbook(wb, file, overwrite = TRUE)


# #------------------------------ Save the TYPE B in a folder ----------------------------------
for (dn in names(Split.TYPE2))
   dnn <- paste0(dn)
  dir.create(dnn)
  sub_Split.TYPE2 <- split(Split.TYPE2[[dn]], Split.TYPE2[[dn]]$Surname)
  for (fn in names(sub_Split.TYPE2))
    file<-file.path(dnn, paste0(fn,".xlsx"))

    d1<-as.data.frame(sub_Split.TYPE2[[fn]])
    wb<-createWorkbook(file)
    addWorksheet(wb, "test", gridLines = T)
    writeData(wb, sheet = "test", x = d1)
    saveWorkbook(wb, file, overwrite = TRUE)
  

数据:

Name    Surname TYPE
John    Greer   A
David   bear    A
Rose    beer    B
Tara    tea     B
Sam     Mac     B
Alan    Glass   B
Brad    Newman  A
Kristen Goodman A
Jessica Goodwin A
Heather Poker   B

【问题讨论】:

标准for 循环不会在R 中并行运行。试试foreach 包中的foreach 函数。 不确定如何为我的代码中的第二个嵌套循环应用 Foreach。嵌套的 Foreach 代码通常紧跟在每个代码之后。我在嵌套循环的第一层有一些事情要做。 【参考方案1】:

因为我没有你的数据,所以我做了一些小的虚拟样本。

我使用的包:

library(tidyverse)
library(openxlsx)
library(foreach)
library(doParallel)

这部分来自你,并没有改变任何东西。

TYPE1 <- rawdata %>% filter(TYPE == "A") 
TYPE2 <- rawdata %>% filter(TYPE == "B") 

Split.TYPE1 <- split(TYPE1, TYPE1$Name) 
Split.TYPE2 <- split(TYPE2, TYPE2$Name) 

定义并行后端。我在这里使用 6 个核心。

cl <- makeCluster(6)
registerDoParallel(cl)

这是您的第一个循环。不要忘记添加.packages = "openxlsx"。这可以确保包裹也被发送给工人。我稍微更改了代码,因为nm in names(Split.TYPE1) 不适用于 foreach。也许有更简单的解决方案,但我不知道。

foreach(nm = 1:length(Split.TYPE1), .combine = cbind, .packages = "openxlsx") %dopar% 
  file <- paste0(names(Split.TYPE1)[nm], ".xlsx")
  d1 <- as.data.frame(Split.TYPE1[[names(Split.TYPE1)[nm]]])

  wb <- createWorkbook(file)
  addWorksheet(wb, "test", gridLines = TRUE)
  writeData(wb, sheet = "test", x = d1)
  saveWorkbook(wb, file, overwrite = TRUE)

第二个循环。我过去只使用过一次,它对我来说效果很好。这就是创建嵌套 foreach 循环的方法。更多信息here。

foreach(dn = 1:length(Split.TYPE2)) %:%
  foreach(fn = 1:length(unique(Split.TYPE2[[names(Split.TYPE2)[dn]]]$Surname)), .packages = "openxlsx") %dopar% 
    dnn <- paste0(names(Split.TYPE2)[dn])
    dir.create(dnn)
    sub_Split.TYPE2 <- split(Split.TYPE2[[names(Split.TYPE2)[dn]]], Split.TYPE2[[names(Split.TYPE2)[dn]]]$Surname)


    file <- file.path(dnn, paste0(names(sub_Split.TYPE2)[fn],".xlsx"))

    d1 <- as.data.frame(sub_Split.TYPE2[[fn]])
    wb <- createWorkbook(file)
    addWorksheet(wb, "test", gridLines = T)
    writeData(wb, sheet = "test", x = d1)
    saveWorkbook(wb, file, overwrite = TRUE)
  

并停止并行后端。

stopCluster(cl)

使用您的数据,我得到以下嵌套循环的文件夹/文件结构:

- Alan
    - Glass.xlsx
- Heather
    - Poker.xlsx
- Rose
    - beer.xlsx
- Sam
    - Mac.xlsx
- Tara
    - tea.xlsx

【讨论】:

感谢您的努力,但是,我在运行您的示例的第二个循环中遇到了一个错误,因为“ 中的错误:任务 1 失败 -”参数意味着不同的行数:10、0” ” 。此外,第二个循环不应该保存每个文件夹中的所有工作表。由于它在原始代码中编码,因此它仅将具有相同姓氏的 split.Type2 文件保存在相关文件夹中。所以我认为你误解了第二个嵌套循环的工作方式。这就是为什么我还不能申请foreach。无论如何都要欣赏它。 您在运行我的示例时遇到了错误?我只是在我的电脑上运行它,没有任何错误。我在 Ubuntu 18.04.3 LTS 系统上使用 R 3.6.2、doParallel 1.0.15、foreach 1.4.7、openxlsx 4.1.4、tidyverse 1.3.0。 我得到的结果与普通 for 循环与 foreach 循环完全相同。我会再试一次。 如果我错了,请纠正我。这在您的嵌套循环中是否正确:(fn in names(Split.TYPE2)),这不应该是(fn in names(sub_Split.TYPE2))吗?我对嵌套的 foreach 循环进行了一些调整。这不是一个好的解决方案。使用嵌套的 foreach 循环,您需要事先准备好迭代器。 感谢您的宝贵时间。在嵌套循环中:第一个循环遍历 split.TYPE2,并根据定义的条件将它们中的每一个拆分为 sub_split.TYPE2,并为每个循环创建一个文件夹。然后内部循环遍历 sup_split.TYPE2 并将相关输出写入相关文件夹中。这有意义吗?

以上是关于R中的并行计算,用于通过循环保存数据的主要内容,如果未能解决你的问题,请参考以下文章

由于空间问题,R中的并行计算

4.0中的并行计算和多线程详解

R语言并行计算中的内存控制

在一组不同的解释变量上并行化 R 中的面板 logit 计算

python--并行计算

R语言高性能编程