如何在R中加速这个循环

时间:2018-02-20 00:15:48

标签: r

我有以下代码,它读取表的列,如果元素包含正确的字符串,它会增加另一个向量中的相应值。这是代码:

dateArray <- integer(365)

for (i in 189500:207097) {
    if (grepl("Jan", csvaryana[i, "Date"], ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)) {
        for (j in 1:31) {
            if (j < 10) {
                if (grepl(paste(sprintf(" 0%d", j), ""), csvaryana[i, "Date"], ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE))
                    dateArray[j] <- dateArray[j] + 1
                }
            if (grepl(paste(sprintf(" %d", j), ""), csvaryana[i, "Date"], ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE))
                dateArray[j] <- dateArray[j] + 1
        }
    }
}

dateArray

请注意,csvaryana是一个包含207,097行的表。该代码应该检查所有行,但我将其减少到只有大约10,000行。运行它需要几分钟,完整代码需要更长的时间。我怎样才能更快地完成同样的事情呢?我听说for循环效率不高。

4 个答案:

答案 0 :(得分:1)

其他答案解释说,R中的

for循环非常慢 如果你想加快你的循环,你可以阅读这篇文章: Strategies to Speed-up R Code

根据文章,您可以执行以下步骤:

  1. 使用ifesle代替if
  2. 使用检查循环外的条件(if语句)的语句
  3. 仅针对True条件运行循环
  4. 使用which()
  5. 使用apply系列函数代替for-loops
  6. 如果您有多核计算机,请使用并行处理
  7. 使用消耗较少内存的数据结构
  8. 如果您了解C ++,那么最好的方法是使用运行C ++代码的Rcpp。

答案 1 :(得分:1)

如果没有一个运行示例,您可以通过将循环的每个元素转换为函数来开始。我们将这些行编号如下:

#1  for (i in 189500:207097) {
#2      if (grepl("Jan", csvaryana[i, "Date"], ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)) {
#3          for (j in 1:31) {
#4              if (j < 10) {
#5                  if (grepl(paste(sprintf(" 0%d", j), ""), csvaryana[i, "Date"], ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE))
#6                      dateArray[j] <- dateArray[j] + 1
#7                  }
#8              if (grepl(paste(sprintf(" %d", j), ""), csvaryana[i, "Date"], ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE))
#9                  dateArray[j] <- dateArray[j] + 1
#10         }
#11     }
#12 }

然后你可以将你的grepl作为一个函数包装起来(这可能比节省时间更美观):

## The grepl function (lines 2, 5 and 8)
grepl.ifelse <- function(i, pattern, data) {
    grepl(pattern, data[i, "Date"], ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
}

对于循环的其他部分,我们可以使用sapply函数将向量的值传递给函数。由于我们只需更新dateArray,因此我们可以使用<<-赋值,将值从函数环境中分配出来(有关详细信息,请参阅?"<<-"):

## Update dateArray function (lines 4 to 10)
update.dateArray <- function(j, i, dateArray, csvaryana) {
    if (j < 10) {
        if (grepl.ifelse(paste(sprintf(" 0%d", j), ""), i, csvaryana)) {
            dateArray[j] <<- dateArray[j] + 1
        }
    } else {
        if (grepl.ifelse(paste(sprintf(" %d", j), ""), i, csvaryana)){
            dateArray[j] <<- dateArray[j] + 1
        }
    }
}

此函数将在函数外更新dateArray(无需返回)。我们可以将相同的原则应用于更大的循环(i):

## Checking the month of January (lines 2 to 11)
check.jan <- function(i, dateArray, csvaryana) {
    if(grepl.ifelse("Jan", i, csvaryana)) {
        ## Update dateArray out of the function
        dateArray <<- sapply(1:31, update.dateArray, i, dateArray, csvaryana)
    }

    return(dateArray)
}

同样,如果没有正在运行的示例,很难进行测试,因此这篇文章可能需要进行一些编辑,但这可能是这样的:

dateArray <- integer(365)

## Running the whole loop
sapply(189500:207097, check.jan, dateArray, csvaryana)
## Updated dateArray
dateArray

答案 2 :(得分:0)

我没有循环就解决了。我将Date列分为Year,Month和Day以及Time,所以我只调用count(Month和Day)并返回一个每月和每天频率的向量:

dateVector <- count(outfile, "X2")

答案 3 :(得分:-1)

请勿使用apply循环。有一个非常好的SO帖子讨论这个: Why are loops slow in R?

答案是对数据框进行矢量化以加快处理速度(通过purrr系列或结帐sprintf(" 0%d", j)。清理数据和代码,以便在循环期间不计算grepl并考虑arr的替代品,因为在这种情况下似乎有点过分。

讨论其中一些概念的好博文: https://robinsones.github.io/Making-R-Code-Faster-A-Case-Study/