【问题标题】:Optimizing For loop with nested if in R在 R 中使用嵌套的 if 优化 For 循环
【发布时间】:2017-09-21 11:39:16
【问题描述】:

我正在尝试将多个 csv 文件合并到一个数据帧中,并尝试使用 for 循环来操作生成的数据帧。生成的数据帧可能有 1,500,000 到 2,000,000 行。

我正在使用下面的代码。

setwd("D:/Projects")
library(dplyr)
library(readr)
merge_data = function(path) 
{ 
  files = dir(path, pattern = '\\.csv', full.names = TRUE)
  tables = lapply(files, read_csv)
  do.call(rbind, tables)
}


Data = merge_data("D:/Projects")
Data1 = cbind(Data[,c(8,9,17)],Category = "",stringsAsFactors=FALSE)
head(Data1)

for (i in 1:nrow(Data1))
{ 
  Data1$Category[i] = ""
  Data1$Category[i] = ifelse(Data1$Days[i] <= 30, "<30",
                       ifelse(Data1$Days[i] <= 60, "31-60",
                       ifelse(Data1$Days[i] <= 90, "61-90",">90")))     

}

但是代码运行了很长时间。有没有更好更快的方法来做同样的操作?

【问题讨论】:

    标签: r for-loop nested-loops


    【解决方案1】:

    我们可以通过从data.table 读取fread 然后使用cut/findInterval 来优化它。当它在多核、服务器上的节点上运行时,这将变得更加明显,fread 利用所有节点并并行执行

    library(data.table)
    merge_data <- function(path) { 
       files = dir(path, pattern = '\\.csv', full.names = TRUE)
      rbindlist(lapply(files, fread, select = c(8, 9, 17)))
     }
    
    Data <- merge_data("D:/Projects")
    Data[, Category := cut(Data1, breaks = c(-Inf, 30, 60, 90, Inf), 
          labels = c("<=30", "31-60", "61-90", ">90"))]
    

    【讨论】:

    • 非常感谢!代码运行良好,运行时间不到几秒钟 :)
    【解决方案2】:

    您已经在使用dplyr,为什么不直接使用:

    Data = merge_data("D:/Projects") %>%
      select(8, 9, 17) %>%
      mutate(Category = cut(Days,
                            breaks = c(-Inf, 30, 60, 90, Inf), 
                            labels = c("<=30", "31-60", "61-90", ">90"))
    

    【讨论】:

      【解决方案3】:

      Akrun 确实是正确的,即 fread 比 read.csv 快得多。

      但是,除了他的帖子之外,我还要补充一点,您的 for 循环是完全没有必要的。他用我不熟悉的 cut/findInterval 替换它。不过,就简单的 R 编程而言,当计算中的某些因素逐行更改时,for 循环是必要的。但是,在您的代码中,情况并非如此,并且不需要 for 循环。

      当您只需要对列运行一次计算时,基本上您正在运行多达 200 万次计算。

      你可以用这样的东西替换你的 for 循环:

      Data1$category = ifelse(Data1$Days <= 30, "<=30",
                       ifelse(Data1$Days <= 60, "31-60",
                       ifelse(Data1$Days <= 90, "61-90",">90")))
      

      你的代码会跑得更快

      【讨论】:

        猜你喜欢
        • 2018-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-27
        • 2020-05-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多