Итеративное вычисление столбцов таблицы данных по одной строке за раз (рекурсивные определения столбцов) - PullRequest
1 голос
/ 17 июня 2020

Фон / Пример

Привет всем,

Я пытаюсь использовать существующие столбцы в таблице data.table для вычисления новых столбцов. Однако столбцы зависят от значения предыдущей строки. Например, скажем, мой столбец R t = A t + B t + R t-1 . У меня есть два столбца, которые составляют мой ключ: сценарий и t . Как я пытался это сделать:

Текущее решение:

for(i in 1:maxScenario){

for(j in 2:nrow(dt)) {

dt[scenario == i & t == j, "R"] <- dt[scenario == i & t == j - 1, "R"]
+ dt[scenario == i & t == j, "A"] + dt[scenario == i & t == j, "B"]

} # end for loop for t

} # end for loop for scenario

Отличие в том, что после «<-» я использую <em>j - 1 вместо j для R , чтобы получить значение предыдущей строки.

Вопрос

Я понимаю, что это добавляет много времени вычислений, и это довольно грубый способ go об этом. Есть ли лучший способ сделать это в пакете data.table? Я пробовал использовать shift () , но столкнулся с проблемами. Использование shift () не «пересчитывает» столбцы на основе A и B .

Я рассмотрел возможность использования рекурсивной формулы, но я не был уверен, что это повлияет на эффективность и время работы. В идеале, я надеюсь запустить около 100K сценария ios и мне понадобятся эти вычисления после того, как будут выполнены сточасти c сценарий ios.

Спасибо!

Изменить: Пример

Вот попытка небольшого примера. Значение R в каждой строке зависит от значения из предыдущей строки.

t  R  A  B
1  0  1  2
2  3  2  3
3  8  2  5
4  15 8  5
5  28 10 8   

Редактировать 2: Дальнейшее уточнение

Наконец-то я смог перевести свою актуальную проблемную функцию в алгебру:

R t = λ * P t + λ * R t-1 - min {λ * P t + λ * R t-1 , D t } - A (t) * max {λ * P t + λ * R t-1 - D t - M t , 0} где P t , D t и M t - другие известные столбцы, а A (t) - индикаторная функция, которая возвращает 0, когда t% 4 равно! = 0, и 1. в противном случае.

Есть ли способ использовать shift () и cumsum () с таким вложенным уравнением?

Ответы [ 3 ]

1 голос
/ 17 июня 2020

Насколько мне известно, невозможно итеративно вычислять строки с помощью встроенных функций из data.table. Я даже считаю, что есть дублирующийся вопрос, в котором есть похожий вопрос (хотя я не могу его найти прямо сейчас).

Однако мы можем ускорить вычисления, отметив уловки, которые мы могли бы использовать в формулировке. Сначала, чтобы получить результат в приведенном примере, мы можем отметить, что это просто cumsum(shift(A, 1, fill = 0) + shift(B, 1, fill = 0))

dt <- fread('t  R  A  B
1  0  1  2
2  3  2  3
3  8  2  5
4  15 8  5
5  28 10 8') 
dt[, R2 := cumsum(shift(A, 1, fill = 0) + shift(B, 1, fill = 0))]
dt
   t  R  A B R2
1: 1  0  1 2  0
2: 2  3  2 3  3
3: 3  8  2 5  8
4: 4 15  8 5 15
5: 5 28 10 8 28

Однако для точно описанной проблемы R t = A t + B t + R t-1 нам нужно быть немного умнее

dt[, R3 := cumsum(A + B) - head(A + B, 1)]
dt
   t  R  A B R2 R3
1: 1  0  1 2  0  0
2: 2  3  2 3  3  5
3: 3  8  2 5  8 12
4: 4 15  8 5 15 25
5: 5 28 10 8 28 43

Что следует из приведенного выше описания. Обратите внимание, что я удаляю первую строку, предполагая, что R<sub>0</sub> = 0, иначе она просто станет cumsum(A + B)

Edit

Поскольку вопрос требует некоторых, возможно, более сложных ситуаций, я ' Я добавлю пример, используя более медленный (но более общий) пример. Идея здесь состоит в том, чтобы использовать функцию set, чтобы избежать промежуточных неглубоких копий (см. help(set) или help("datatable-optimize")).

dt[, R4 := 0]
for(i in seq.int(2, dt[, .N])){
  #dummy complicated scenario
  f <- dt[seq(i), lm(A ~ B - 1)]
  set(dt, i, 'R4', unname(unlist(coef(f))))
}
dt
t  R  A B R2 R3        R4
1: 1  0  1 2  0  0 0.0000000
2: 2  3  2 3  3  5 0.6153846
3: 3  8  2 5  8 12 0.4736842
4: 4 15  8 5 15 25 0.9206349
5: 5 28 10 8 28 43 1.0866142
1 голос
/ 18 июня 2020

Вот вариант, использующий Rcpp с data.table, поскольку его легче думать / кодировать в cpp для рекурсивного уравнения:

DT[, A := +(t %% 4 == 0)]

library(Rcpp)    
cppFunction('NumericVector recur(double lambda, NumericVector P, 
    NumericVector D, NumericVector M, NumericVector A) {
        int sz = P.size(), t;
        NumericVector R(sz);

        for (t=1; t<sz; t++) {
            R[t] = lambda * P[t] + lambda * R[t-1] -
                std::min(lambda * P[t] + lambda * R[t-1], D[t]) -
                A[t] * std::max(lambda * P[t] * lambda * R[t-1] - D[t] - M[t], 0.0);
        }

    return(R);
}')

DT[, R := recur(lambda, P, D, M, A)]

вывод:

     t            P           D          M A           R
 1:  1  1.262954285  0.25222345 -0.4333103 0  0.00000000
 2:  2 -0.326233361 -0.89192113 -0.6494716 0  0.72880445
 3:  3  1.329799263  0.43568330  0.7267507 0  0.59361856
 4:  4  1.272429321 -1.23753842  1.1519118 1  1.89610128
 5:  5  0.414641434 -0.22426789  0.9921604 0  1.37963924
 6:  6 -1.539950042  0.37739565 -0.4295131 0  0.00000000
 7:  7 -0.928567035  0.13333636  1.2383041 0  0.00000000
 8:  8 -0.294720447  0.80418951 -0.2793463 1  0.00000000
 9:  9 -0.005767173 -0.05710677  1.7579031 0  0.05422319
10: 10  2.404653389  0.50360797  0.5607461 0  0.72583032
11: 11  0.763593461  1.08576936 -0.4527840 0  0.00000000
12: 12 -0.799009249 -0.69095384 -0.8320433 1 -1.23154792
13: 13 -1.147657009 -1.28459935 -1.1665705 0  0.09499689
14: 14 -0.289461574  0.04672617 -1.0655906 0  0.00000000
15: 15 -0.299215118 -0.23570656 -1.5637821 0  0.08609900
16: 16 -0.411510833 -0.54288826  1.1565370 1  0.38018234

данные:

library(data.table)    
set.seed(0L)
nr <- 16L
DT <- data.table(t=1L:nr, P=rnorm(nr), D=rnorm(nr), M=rnorm(nr))
lambda <- 0.5
1 голос
/ 17 июня 2020

Это создает новый столбец R2 с теми же значениями, что и R

DT[, R2 := shift( cumsum(A+B), type = "lag", fill = 0 ) ][]

#    t  R  A B R2
# 1: 1  0  1 2  0
# 2: 2  3  2 3  3
# 3: 3  8  2 5  8
# 4: 4 15  8 5 15
# 5: 5 28 10 8 28
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...