В R - вычисление значений в векторе с использованием процентных возвратов - PullRequest
1 голос
/ 12 июля 2020

У меня есть таблица data.table, состоящая из одной строки со значением и списком процентных возвратов. Я хочу преобразовать эту информацию в значения, последовательно используя процентные доходы. Ниже приведен пример. Он работает нормально, но я хочу ускорить его и сделать более эффективным. В примере я показываю всего четыре периода возврата ... на практике у меня их несколько сотен. Кроме того, мне нужно сделать это преобразование несколько десятков тысяч раз параллельно. Так что любое ускорение поможет. Есть ли какая-либо функция / пакет R, которые могут сделать что-то подобное более эффективно? Спасибо за уделенное время!

library(data.table)
library(tidyr)
a <- 2
b <- 4
x <- data.table(awq = c(0.1), R1 = c(0.15), R2 = c(-0.05), R3 = c(0.70), R4 = c(-0.1))
print(x)
    awq   R1    R2  R3   R4
 1: 0.1 0.15 -0.05 0.7 -0.1
tmp1 <- as.data.table(tidyr::gather(x, period, return, -awq, factor_key=F))
setnames(tmp1, old = c("return"), new = c("ret"))
tmp1[period == "R1", v := awq*ret + awq]
for(i in 2:4) {
  tmp1[i, v := tmp1[i, ret] * abs(tmp1[(i-1), v]) + tmp1[(i-1), v]]
}
tmp1[v < 0, v := 0]
tmp1 <- tmp1[, .(period, v)]
tmp1[, a := a]
tmp1[, b := b]
tmp1 <- as.data.table(pivot_wider(tmp1, names_from = period, values_from = c(v)))
print(tmp1)
   a b    R1      R2       R3        R4
1: 2 4 0.115 0.10925 0.185725 0.1671525

Ответы [ 2 ]

1 голос
/ 12 июля 2020

Кроме того, мы могли бы также использовать rowCumprods из matrixStats

library(matrixStats)
library(data.table)
x[, names(x)[-1] := as.data.table(awq * rowCumprods( as.matrix(.SD+ 1))), 
      .SDcols = patterns("^R")][]
#  awq    R1      R2       R3        R4
#1: 0.1 0.115 0.10925 0.185725 0.1671525

Кроме того, в исходном скрипте, показанном OP, gather/pivot_wider можно заменить на melt/dcast из data.table

tmp1 <- melt(x, id.var = 'awq', variable.name = 'period', value.name = 'ret')
tmp1[period == "R1", v := awq*ret + awq]

, а for l oop можно заменить на Reduce

tmp1[, v := Reduce(`*`, ret + 1, accumulate = TRUE) * awq]
dcast(tmp1, awq ~ period, value.var = 'v')
#   awq    R1      R2       R3        R4
#1: 0.1 0.115 0.10925 0.185725 0.1671525
1 голос
/ 12 июля 2020

комментарий @ akrun к cumprod точен:

myfunc <- function(awq, rest) as.data.table(awq * t(apply(1 + rest, 1, cumprod)))

### Another row
x <- data.table(awq = c(0.1,0.2), R1 = c(0.15), R2 = c(-0.05), R3 = c(0.70), R4 = c(-0.1))

x[, myfunc(awq, .SD[,R1:R4])]
#         R1      R2       R3        R4
# [1,] 0.115 0.10925 0.185725 0.1671525
# [2,] 0.230 0.21850 0.371450 0.3343050

Вы можете увеличить исходный кадр с помощью пары техник:

cbind(x, x[, myfunc(awq, .SD[,R1:R4])])
#    awq   R1    R2  R3   R4    R1      R2       R3        R4
# 1: 0.1 0.15 -0.05 0.7 -0.1 0.115 0.10925 0.185725 0.1671525
# 2: 0.2 0.15 -0.05 0.7 -0.1 0.230 0.21850 0.371450 0.3343050

x[, c("S1","S2","S3","S4") := myfunc(awq, .SD[,R1:R4]) ][]
#    awq   R1    R2  R3   R4    S1      S2       S3        S4
# 1: 0.1 0.15 -0.05 0.7 -0.1 0.115 0.10925 0.185725 0.1671525
# 2: 0.2 0.15 -0.05 0.7 -0.1 0.230 0.21850 0.371450 0.3343050

Первый имеет недостаток в виде повторяющихся имен столбцов. Последний имеет тот недостаток, что необходимо знать количество задействованных столбцов априори.

myfunc в основном состоит из фанеры, это можно упростить дополнительно:

x[, awq*t(apply(1+.SD[,R1:R4],1,cumprod)) ]
#         R1      R2       R3        R4
# [1,] 0.115 0.10925 0.185725 0.1671525
# [2,] 0.230 0.21850 0.371450 0.3343050

со всеми этапами «увеличения», о которых я упоминал ранее.

...