加快R中大数据的for循环处理时间

Posted

技术标签:

【中文标题】加快R中大数据的for循环处理时间【英文标题】:Speed up the processing time of for loop for big data in R 【发布时间】:2022-01-12 15:04:03 【问题描述】:

我有非常大的数据集bdd_cases 有 150,000 行,bdd_control 有 1500 万行。在这里,为了简单起见,我减小了这些数据集的大小并作为驱动链接给出。除其他外,我正在尝试根据cluster_casesubset 变量将匹配行从bdd_control 添加到bdd_cases

我为此目的编写了以下for loop,它非常适合此处给出的小型数据集示例。即使是这个小数据集也需要大约 13 秒。

#import data
id1 <- "199TNlYFwqzzWpi1iY5qX1-M11UoC51Cp"
id2 <- "1TeFCkqLDtEBz0JMBHh8goNWEjYol4O2z"

bdd_cases <- as.data.frame(read.csv(sprintf("https://docs.google.com/uc?id=%s&export=download", id1)))
bdd_control <- as.data.frame(read.csv(sprintf("https://docs.google.com/uc?id=%s&export=download", id2)))

#declare empty dataframe
bdd_temp <- NULL
list_p <- unique(bdd_cases$cluster_case)

#for loop
for (i in 1:length(list_p)) 

  temp <- bdd_cases %>% 
    filter(cluster_case==list_p[i])                                  #select the first case from bdd_cases
  
  temp0 <- bdd_control %>% filter(subset==temp$subset)               #select the rows from bdd_control that match the first case above on the subset variable
  
  temp <- rbind(temp, temp0)                                         #bind the two

  temp$cluster_case <- list_p[i]                                     #add the ith cluster_case to all the rows 
  
  temp <- temp %>%
    group_by(cluster_case) %>% #group by cluster case
    mutate(age_diff = abs(age - age[case_control=="case"]),          #calculate difference in age between case and controls
           fup_diff = foll_up - foll_up[case_control=="case"],       #calculate difference in foll_up between case and controls
           age_fup = ifelse(age_diff<=2 & fup_diff==0,"accept","delete")) %>% #keep the matching controls and remove the other controls for the ith cluster_case
    filter(age_fup=="accept") %>% 
    select(-age_fup)
  
  bdd_temp <- bdd_temp %>% # finally add this matched case and control to the empty dataframe
    bind_rows(temp)


当我为具有数百万行的原始数据集尝试相同的 for loop 时,我的问题出现了。我的程序已经运行了 2 天。我在 R studio server 上运行它,它有 64 个内核和 270 GB RAM。

我参考过以前的帖子,比如这个 (Speed up the loop operation in R),它讨论了矢量化和使用列表而不是数据帧。但是,我无法将这些应用于我的具体情况。

我可以对for loop 中的命令进行任何具体改进以加快执行速度吗?

速度上的任何微小改进都将意味着很多。谢谢。

【问题讨论】:

一些观察:在循环中存在不必要的数据分配和复制,使用 rbinddplyr - 它的速度并不为人所知。 dtplyrcollapsedata.table(按努力顺序)可能会大大加快这个循环的速度。进一步 - 初始化变量并使用快速 I/O 方法,如 vroomfreaddata.table 您是否尝试过与 foreach %dopar% 并行运行? 听起来微不足道,但最好的速度改进是减少问题的大小。想想你是否可以预先过滤你的 15Mill 数据集,例如在去 R 之前使用低级工具进行噪声过滤等。 【参考方案1】:

这应该会大大加快速度。

在我的系统上,速度增益大约是 5 倍。

#import data
id1 <- "199TNlYFwqzzWpi1iY5qX1-M11UoC51Cp"
id2 <- "1TeFCkqLDtEBz0JMBHh8goNWEjYol4O2z"

library(data.table)
# use fread for reading, fast and get a nice progress bar as bonus
bdd_cases <- fread(sprintf("https://docs.google.com/uc?id=%s&export=download", id1))
bdd_control <- fread(sprintf("https://docs.google.com/uc?id=%s&export=download", id2))
#Put everything in a list
L <- lapply(unique(bdd_cases$cluster_case), function(x)
  temp <- rbind(bdd_cases[cluster_case == x, ],
                bdd_control[subset == bdd_cases[cluster_case == x, ]$subset])
  temp[, cluster_case := x]
  temp[, `:=`(age_diff = abs(age - age[case_control=="case"]),
              fup_diff = foll_up - foll_up[case_control=="case"])]
  temp[age_diff <= 2 & fup_diff == 0, ]
)
#Rowbind the list
final <- rbindlist(L, use.names = TRUE, fill = TRUE)

【讨论】:

您好,感谢您的解决方案,但是当我尝试运行它时,会弹出以下错误。 [.data.frame(bdd_cases, cluster_case == x, ) 中的错误:找不到对象 'cluster_case' 调用方:[.data.frame(bdd_cases, cluster_case == x, ) 数据必须是data.table。你有没有使用fread()读入数据? 亲爱的@Wimpel,它有效!如果我完全运行您的代码。我必须使用setDT 将类转换为 data.table。如果大型数据集有任何时间改进,我会告诉你。非常感谢付出的努力和时间。

以上是关于加快R中大数据的for循环处理时间的主要内容,如果未能解决你的问题,请参考以下文章

R中大数据的计数算法

如何在处理 r 中超过 500 万个观测值的数据框时加快迭代速度?

在R中没有for循环的行之间移动值

R:循环处理大数据集(GB)的块?

怎样向SQL Server中大批量插入数据

R中大文件的数据格式是啥?