r - 开发引用 n-1 行的更有效的 for 循环

标签 r

我需要运行一个逻辑,其中“n”步骤基于“n-1”步骤的结果,因此我在 for 循环中实现了逻辑。这里的代码:

library(data.table)
df<-as.data.table(matrix(rexp(100000, rate=.1), ncol=1000))
weight<-as.data.table(matrix(rexp(100, rate=10), ncol=1))

for (row in 1:nrow(weight))
{
  if (row > 2){

    # from second row start the logic
    # We create weighted averages of variables values: value(n-1)* (1-weight) + value(n) * weight
    df[row] <- 
      df[row-1,] * as.numeric(1 - weight[row]) + df[row,] * as.numeric(weight[row])

  }
}

但是,它需要很长时间才能运行,因为 data.table 实际上由 1098 列和 200k 行组成。

有人对如何开发更有效的解决方案有想法吗?

最佳答案

让我解释一下我将如何提高此类任务的性能的故事。

基本时间

首先,我重新创建您的数据(稍微小一点)并测量运行它所需的时间:

library(data.table)
library(tictoc) # for timing only

# easier way to create a data.table
NROWS <- 100
NCOLS <- 100
set.seed(123)

df_orig <- data.table(matrix(rexp(NROWS * NCOLS, rate = 0.1), ncol = NCOLS))
wt <- data.table(V1 = rexp(NROWS, rate = 10))


df1 <- copy(df_orig)
tic()
for (r in 1:nrow(wt)) {
  if (r >= 2) { # assuming you mean >= 2 not >= 3 (:= >2)
    # from second row start the logic
    # We create weighted averages of variables values: value(n-1)* (1-weight) + value(n) * weight
    df1[r, ] <- df1[r-1,] * as.numeric(1 - wt[r]) + df1[r,] * as.numeric(wt[r])
  }
}
toc()
#> 1.274 sec elapsed

reprex package 创建于 2020-05-08| (v0.3.0)

模型 2

然后我研究改进代码的方法。例如,as.numeric()可能很贵,if ()检查循环。让我们删除它,但确保结果保持不变

# no if check in the loop and replace as.numeric with [[1]]
# loop only from 2
df2 <- copy(df_orig)
tic()
for (r in 2:nrow(wt)) {
  df2[r, ] <- df2[r - 1, ] * (1 - wt[r][[1]]) + df2[r, ] * wt[r][[1]]
}
toc()
#> 1.149 sec elapsed

# check that the results are identical
all.equal(df1, df2)
#> [1] TRUE

reprex package 创建于 2020-05-08| (v0.3.0)

好一点,但我们还没有。

矩阵模型

一般data.table是提高速度的好方法,但这种访问最好在基础结构中实现,即 matrix() .

所以让我们这样做:

# Matrix based
mdf <- as.matrix(df_orig)
mwt <- as.matrix(wt)

tic()
for (r in 2:nrow(wt)) {
  mdf[r, ] <- mdf[r - 1, ] * (1 - mwt[r, ]) + mdf[r, ] * mwt[r, 1]
}
toc()
#> 0.005 sec elapsed

df3 <- data.table(mdf)

all.equal(df3, df1)
#> [1] TRUE

reprex package 创建于 2020-05-08| (v0.3.0)

这看起来是一个不错的加速!

但还有更多...
Rcpp模型

尤其是在这类任务中,Rcppc++写起来更麻烦,但提供了漂亮的加速。
这里我们使用 Armadillo c++ 的矩阵库及其Rcpp RcppArmadillo 的绑定(bind).

将您的代码翻译成 Rcpp产生这个:

# using rcpp
rcpp_code <- "// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
// [[Rcpp::export]]
arma::mat my_rcpp_fun(arma::mat data, arma::mat weights) {
  const int len = weights.size();

  // Cpp starts indexing at 0, so 1 is the second row!
  for (int i = 1; i < len; i++) {
    data.row(i) = data.row(i - 1) * (1 - weights.row(i)(0)) + data.row(i) * weights.row(i)(0);
  }
  return data;
}
"

Rcpp::sourceCpp(code = rcpp_code)
mdf2 <- as.matrix(df_orig)
mwt2 <- as.matrix(wt)

tic()
mdf4 <- my_rcpp_fun(mdf2, mwt2)
toc()
#> 0.002 sec elapsed

df4 <- data.table(mdf4)
all.equal(df4, df1)
#> [1] TRUE

reprex package 创建于 2020-05-08| (v0.3.0)

从 1.274s 到 0.002s 听起来不错吧?!

关于r - 开发引用 n-1 行的更有效的 for 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61673970/

相关文章:

r - xts 赋值更改列类

regex - 从 R 中的模型公式中删除包装在函数中的变量

r - 在函数内使用 ggplot 创建的绘图出现多图错误

删除重复项并忽略小数点

r - 识别数据框中的重复列

r - 如何使用 R 的 {collapse} 包来实现正确的 fgroup_by() |> ftransform() 输出?

r - 在数据框内扩展数据框

r - dplyr 重新编码 qoute 元素

R,迭代矩阵的行向量

r - 如何在 ggplot2 中使用 facet_grid 获得适合的长标签?