Позвольте мне объяснить свою историю, как go я бы увеличил производительность такой задачи.
Базовое время
Сначала я воссоздаю ваши данные (немного меньше) и измеряю время, необходимое для его запуска:
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
Создано 08.05.2020 с помощью пакета реплекс (v0.3.0)
Модель 2
Потом смотрю способы улучшить код. Например, as.numeric()
может быть дорогим, как и чек if ()
в l oop. Давайте удалим это, но убедимся, что результаты остались прежними.
# 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
Создано 08.05.2020 с помощью пакета репекс (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
Создано 08.05.2020 с помощью пакета реплекс (v0.3.0)
Похоже, это неплохое ускорение!
Но есть еще кое-что ...
Rcpp
Модель
Особенно в таких задачах Rcpp
и c++
более громоздки для написания, но обеспечивают прекрасное ускорение. Здесь мы используем матричную библиотеку Armadillo
для c++
и ее Rcpp
привязок RcppArmadillo
.
Преобразование вашего кода в 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
Создано 2020-05-08 пакетом REPEX (v0.3.0)
От 1,274 с до 0,002 s звучит хорошо, правда?!