每次迭代都取决于先前迭代的结果时的优化R循环

时间:2018-06-21 21:00:45

标签: r for-loop optimization

我需要优化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处的值。我想不出更好的解决方案。

有人知道一个更聪明的人吗?

2 个答案:

答案 0 :(得分: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值的初始条件。最后,我们修剪掉虚拟的第一个值。

答案 1 :(得分:2)

@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 s,而C ++解决方案DT3是“即时”的。两者之间有一个小优化,您可以在R中进行:不要在循环内写入data.frame。您最好写一个向量,最后添加它。这是profvisDT的{​​{1}}的输出:

profiler

尽管如此,它仍然比C ++慢得多。