【问题标题】:How to speed up forloop grep in a large dataframe using R如何使用 R 在大型数据帧中加速 forloop grep
【发布时间】:2021-04-25 22:18:35
【问题描述】:

我需要帮助。

我的脚本适用于许多数据帧,即使这需要几个小时(在集群上:> 100 GB 内存)。对于一些大型数据帧(> 300 万行),即使运行两天后,for 循环也不起作用。 因此,如果有办法加快 for 循环或用 R 中更多的加速器函数替换脚本,我需要帮助。

这是对我的脚本/数据的简短描述:

    snp1 <- c("R0100004", "R0100009", "R0100044", "R0100061", "R0100066","R0100067") # 3 million SNPs
    snp2 <- c("R0100039", "R0100152", "R0100066", "R0100067", "R0100068", "R0100082") # 3 million SNPs
    
    blocks <- c("R0100004|R0100009|R0100190|R0100015|R0100016|R0100017|R0100018|R0100021|R0100022|R0100024|R0100025",
                "R0100039|R0100038|R0100037|R0100036|R0100043|R0100044",                                   
                "R0100220|R0100052|R0100053|R0100054|R0100055|R0100057|R0100058|R0100059",                         
                "R0100061|R0100066|R0100067",                                                                      
                "R0100068|R0100069|R0100071|R0100072|R0100073|R0100074|R0133440|R0100076|R0100077|R0100078",         
                "R0100079|R0100081|R0100082") # 50000 haplotype block: each block contain > 2 SNPs. The SNP could be in 2 or more blocks.

# THE OBJECTIVE: For each SNP (snp1 and snp2) find his haplotype block    
    # This is my forloop
    I <- length(snp1) # 3000000
    res1 <- list()
    res2 <- list()
    for(j in 1:I){ 
        myres1 <- list(grep(snp1[j], blocks, value=T))
        myres2 <- list(grep(snp2[j], blocks, value=T))
        res1[j] <- myres1
        res2[j] <- myres2
    }

for 循环适用于中等数据帧,但对于具有大行的数据帧则需要几天时间。 如何替换或加快这个 for 循环?

提前致谢。

【问题讨论】:

  • 这可能是XY problem。你想回答/解决什么问题?
  • 我正在尝试的是:对于每个 snp(snp1 和 snp2)从数据帧(块)中确定其块。
  • 我的回答能达到你的要求吗?
  • @Jakub.Novotny 我得到 by` 不能包含 RHS 中缺少的连接列 blocks2 错误。你知道错误的来源吗?谢谢
  • 没有线索。我刚刚在另一台计算机上运行了代码,它没有任何问题。

标签: r loops for-loop data.table


【解决方案1】:

这会很快,但可能不会完全符合您的要求。

我根据示例数据假设了一些事情 - 例如每个snp1snp2 值最多只出现在一个块中,并且blockssnp1snp2 短得多。

library(data.table)
y <- data.table(block = blocks)[,
  .(Rnum = unlist(strsplit(block, "\\|"))),
  .(block)]
res1 <- as.list(y[snp1, on = 'Rnum', block])
res2 <- as.list(y[snp2, on = 'Rnum', block])

如果我的假设有误,请告诉我。

但是,我强烈怀疑,如果您描述您实际尝试做的事情,将会有比这更好的解决方案。

【讨论】:

  • 谢谢。事实上,snp 可能存在于许多其他集团中。块由几个snp组成。 @Jakub 的解决方案效果很好。
【解决方案2】:

我尝试了完全不同的方法,并故意将您的数据大小增加了 500。我提出的解决方案是对 500 倍大的数据集快 5 倍,如果数据大小增加,应该会越来越快。

library(tidyverse)

scale_factor <- 500 # to basically make your objects larger

# recreating your objects, but scaling them using the scaling_factor
snp1 <- c("R0100004", "R0100009", "R0100044", "R0100061", "R0100066","R0100067") %>% rep(scale_factor)
snp2 <- c("R0100039", "R0100152", "R0100066", "R0100067", "R0100068", "R0100082") %>% rep(scale_factor)

blocks <- c("R0100004|R0100009|R0100190|R0100015|R0100016|R0100017|R0100018|R0100021|R0100022|R0100024|R0100025",
            "R0100039|R0100038|R0100037|R0100036|R0100043|R0100044",                                   
            "R0100220|R0100052|R0100053|R0100054|R0100055|R0100057|R0100058|R0100059",                         
            "R0100061|R0100066|R0100067",                                                                      
            "R0100068|R0100069|R0100071|R0100072|R0100073|R0100074|R0133440|R0100076|R0100077|R0100078",         
            "R0100079|R0100081|R0100082")

# this is your original code
original <- function(snp1, snp2, blocks){
  # This is my forloop
  I <- length(snp1) # 3000000
  res1 <- list()
  res2 <- list()
  for(j in 1:I){ 
    myres1 <- list(grep(snp1[j], blocks, value=T))
    myres2 <- list(grep(snp2[j], blocks, value=T))
    res1[j] <- myres1
    res2[j] <- myres2
  }
  
}

tuned <- function(snp1, snp2, blocks){
  
  df1 <- data.frame(snp1)
  df2 <- data.frame(snp2)
  
  df_blocks <- data.frame(blocks) %>%
    mutate(blocks2 = blocks %>% str_split("\\|")) %>%
    unnest(cols = c(blocks2))
  
  res1 <- left_join(df1, df_blocks, by = c("snp1" = "blocks2")) %>%
    pull(blocks) %>%
    map(~.x)
  
  res2 <- left_join(df2, df_blocks, by = c("snp2" = "blocks2")) %>%
    pull(blocks) %>%
    map(~.x)

  
}

# this benchmarks both solutions
library(microbenchmark)
microbenchmark(original(snp1, snp2, blocks), tuned(snp1, snp2, blocks))
# Unit: milliseconds
# expr     min       lq     mean  median       uq      max neval
# original(snp1, snp2, blocks) 63.0723 65.65775 71.18956 69.3941 73.14755 102.5951   100
# tuned(snp1, snp2, blocks) 13.3522 14.39015 16.24824 15.5053 16.17280  33.6679   100

【讨论】:

  • 谢谢,我会试试的。但就运行时间而言,我认为这对我的真实数据帧(> 300 万行)不会有太大影响。
  • Ab_Lh:我完全改变了我的答案。现在它应该比处理更大数据集的解决方案更快。
  • 我尝试运行脚本,但仍然收到错误消息:res1 &lt;- left_join(df1, df_blocks, by = c("snp1" = "blocks2")) %&gt;% + pull(blocks) %&gt;% + map(~.x) Erreur : by` can't contain join column blocks2 which is missing from RHS`
  • 谢谢。实际上,服务器中的R版本存在问题。我更新了一些软件包并将stringsAsFacto=FALSE 添加到data.frame(blocks)
  • 乐于助人 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-12
  • 2013-01-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多