【问题标题】:Optimization R loop when each iteration is dependent on the results of previous iterations每次迭代都依赖于先前迭代的结果时的优化 R 循环
【发布时间】:2018-12-01 07:22:24
【问题描述】:

我需要优化一个 R 脚本。特别是,我需要加快或删除一些脚本的淹没周期。我已经定义了许多循环类型:

DT <- data.frame("x"=c(1:20),
                 "y"=c(20:1))
DT$vect[1] <- DT$y[1]
for (i in 2:20) {
  DT$vect[i] <- DT$vect[i-1] * DT$x[i] - DT$x[i-1] * (1 + DT$y[i]) 
}

因为要计算位置i 的值,所以需要知道位置i-1。我想不出更好的解决方案。

有人知道更聪明的吗?

【问题讨论】:

    标签: r for-loop optimization


    【解决方案1】:

    @MrFlick 的解决方案非常好,但如果您更熟悉for 循环并且不介意混用另一种语言,您可以试试 Rcpp。这种类型的循环是 C++ 更高效的一个典型例子:

    #include <Rcpp.h>
    using namespace Rcpp;
    
    // [[Rcpp::export]]
    NumericVector forLoop(DataFrame dt) {
      int N = dt.nrow();
      NumericVector x = dt["x"];
      NumericVector y = dt["y"];
      NumericVector vec(N, y(0));
      for (int i = 1; i < N; ++i) {
        vec(i) = vec(i-1) * x(i) - x(i-1) * (1 + y(i));
      } 
      return vec;
    }
    
    /*** R
    
    N <- 20000
    DT <- data.frame("x"=c(1:N),
                     "y"=c(N:1))
    DT$vect[1] <- DT$y[1]
    system.time({
      for (i in 2:N) {
        DT$vect[i] <- DT$vect[i-1] * DT$x[i] - DT$x[i-1] * (1 + DT$y[i]) 
      }
    })
    DT2 <- data.frame("x"=c(1:N),
                     "y"=c(N:1))
    vect <- vector("numeric", length = N)
    vect[1] <- DT2$y[1]
    system.time({
      for (i in 2:N) {
        vect[i] <- vect[i-1] * DT2$x[i] - DT2$x[i-1] * (1 + DT2$y[i]) 
      }
      DT2$vect <- vect
    })
    
    all.equal(DT, DT2)
    
    DT3 <- data.frame("x"=c(1:N),
                     "y"=c(N:1))
    system.time({
      vect <- forLoop(DT3)
      DT3$vect <- vect
    })
    all.equal(DT, DT3)
    */
    

    原始循环在我的机器上需要 1.5 秒,而 C++ 解决方案 DT3 是“即时的”。在这两者之间,您可以在 R 中做一个小的优化:不要在循环内写入 data.frame。你最好写入一个向量并在最后添加它。这里是profvisDTDT2 的输出:

    但仍然比 C++ 慢得多。

    【讨论】:

    • 为您的解决方案!我正在考虑使用 Rcpp。
    【解决方案2】:

    它可能没有那么漂亮,但你可以使用dplyrpurrr 来做一个reduce 类型的函数。

    DT %>% 
      select(x,y) %>% 
      mutate(prevx=lag(x, default=-1)) %>% 
      transpose() %>% 
      accumulate(function(prev, xx) {
        prev * xx$x - xx$prevx*(1+xx$y)
      }, .init=-1/DT$x[1]) %>% 
      tail(-1)
    #  [1] 2.000000e+01 2.000000e+01 2.200000e+01 3.400000e+01 1.020000e+02
    #  [6] 5.320000e+02 3.634000e+03 2.897400e+04 2.606620e+05 2.606512e+06
    # [11] 2.867152e+07 3.440582e+08 4.472756e+09 6.261858e+10 9.392787e+11
    # [16] 1.502846e+13 2.554838e+14 4.598709e+15 8.737547e+16 1.747509e+18
    

    我们使用lag() 函数将x[i]x[i-1] 放在同一行上。我们使用transpose 来获取我们可以迭代的命名值列表。然后accumulate() 允许使用将函数的输出插入回自身作为输入,并在此过程中跟踪值。在这里,我们插入提供的公式并使用一个特殊的初始值,该值满足您给出的第一个值等于第一个 y 值的初始条件。最后我们修剪掉第一个虚拟值。

    【讨论】:

    • @hello MrFlik,感谢您提供非常精致的解决方案!
    猜你喜欢
    • 2021-04-12
    • 2017-11-07
    • 2016-05-29
    • 1970-01-01
    • 1970-01-01
    • 2013-05-29
    • 1970-01-01
    • 2020-03-03
    • 2021-10-05
    相关资源
    最近更新 更多