我需要运行一个逻辑,其中“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
模型尤其是在这类任务中,
Rcpp
和 c++
写起来更麻烦,但提供了漂亮的加速。这里我们使用
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/