Каков наилучший способ векторизации итеративной функции в R? - PullRequest
0 голосов
/ 27 января 2019

Я работаю с функцией, которая цитирует стоимость портфеля данного человека. Чтобы работать с этой функцией, мне нужно перебрать строки в фрейме данных и применить эту функцию:

x$cota <- 100

cotiza <- function(x){
  for(i in 1:nrow(x)) {
    if (i > 1){
      x[i,]$cota <- ((x[i,]$pl - x[i,]$mov)/x[i-1,]$pl) * x[i-1,]$cota
    }
  return (x)
}

Это тот фрейм данных, к которому применяется функция:

    data    pl     mov cota
1 2018-01-01 500.0  250000  100
2 2018-01-02 525.0       0  100
3 2018-01-03 997.2 -100000  100
4 2018-01-04 500.0       0  100
5 2018-01-05 520.0       0  100

Вывод функции на этом фрейме данных должен выглядеть следующим образом:

    data    pl     mov     cota
1 2018-01-01 500.0  250000   100.00
2 2018-01-02 525.0       0   105.00
3 2018-01-03 997.2 -100000 20199.44
4 2018-01-04 500.0       0 10128.08
5 2018-01-05 520.0       0 10533.20

Есть ли способ сделать эту функцию векторизованной, чтобы я мог применить ее к кадру данных?

Ответы [ 2 ]

0 голосов
/ 27 января 2019

1) Используя воспроизводимый ввод в примечании в конце, попробуйте использовать cumprod, например:

cotiza2 <- function(x) {
  n <- nrow(x)
  if (n < 2) return(x)
  transform(x, cota = cumprod(c(cota[1], (pl - mov)[-1] / pl[-n])))
}

cotiza2(x)
##         data    pl     mov     cota
## 1 2018-01-01 500.0  250000   100.00
## 2 2018-01-02 525.0       0   105.00
## 3 2018-01-03 997.2 -100000 20199.44
## 4 2018-01-04 500.0       0 10128.08
## 5 2018-01-05 520.0       0 10533.20

2) Это не векторизовано, но оно короче кода в вопросе и менее сложно, чем (1).

cotiza3 <- function(x) {
  n <- nrow(x)
  if (n < 2) return(x)
  within(x, for(i in 2:n) cota[i] <- (pl[i] - mov[i]) / pl[i-1] * cota[i-1])
}

cotiza3(x)
##         data    pl     mov     cota
## 1 2018-01-01 500.0  250000   100.00
## 2 2018-01-02 525.0       0   105.00
## 3 2018-01-03 997.2 -100000 20199.44
## 4 2018-01-04 500.0       0 10128.08
## 5 2018-01-05 520.0       0 10533.20

Примечание

Lines <- "
    data    pl     mov cota
1 2018-01-01 500.0  250000  100
2 2018-01-02 525.0       0  100
3 2018-01-03 997.2 -100000  100
4 2018-01-04 500.0       0  100
5 2018-01-05 520.0       0  100"
x <- read.table(text = Lines)

Обновление

Добавлено (1).

0 голосов
/ 27 января 2019

Поскольку cota[3] полагается на обновленное значение cota[2], которое опирается на cota[1], вы не можете выполнить простую векторизацию этой функции. Иногда вы можете обмануть, используя cumsum, cumprod или аналогичные кумулятивные функции (которые все еще итеративны, но в действительно оптимизированном коде), но это зависит от «простого итеративного накопления». В этом случае, однако, последовательность

cota[2] = cota[1] * (pl[2] - mov[2]) / pl[1],
cota[3] = cota[2] * (pl[3] - mov[3]) / pl[2]

если вы замените cota[2], вы получите

(cota[1] * (pl[2] - mov[2]) / pl[1]) * (pl[3] - mov[3]) / pl[2]

что эффективно

cota[1] * (pl[2]*pl[3] - pl[2]*mov[3] - pl[3]*mov[2] + mov[2]*mov[3]) / (pl[1] * pl[2])

, который не сразу поддается простым кумулятивным операторам.

Некоторые функции, обеспечивающие своего рода подвижные окна, в частности zoo::rollapply, но часто они делают петлю for под капотом. (Технически, я считаю, что большинство *apply функций также делают for петли под капотом, хотя, вероятно, хорошо под капотом.)

Если у вас есть проблемы с производительностью этой или подобных функций, вы всегда можете использовать Rcpp или аналогичные ускорения. (Как только вы попадете на Rcpp -территорию, вы можете обнаружить, что необработанный цикл for превзойдет векторизованный код, даже Rcpp -нативную векторизацию, хотя это во многом зависит от многих других вещей.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...