Петли в R - Нужно использовать индекс, во всяком случае, чтобы избежать "для"?

Я знаю, что это не лучшая практика в R, чтобы использовать цикл for, потому что он не имеет улучшенной производительности. Для почти всех случаев существует функция семейства *apply, которая решает наши проблемы.

Однако я столкнулся с ситуацией, когда не вижу обходного пути.

Мне нужно рассчитать процентное изменение для следующих значений:

pv[1] <- 0
for(i in 2:length(x)) {
  pv[i] <- (x[i] - x[i-1])/x[i-1]
}

Итак, как вы можете видеть, мне нужно использовать как элемент x[i], так и элемент x[i-1]. Используя функции *apply, я просто вижу, как использовать x[i]. В любом случае, я могу избежать циклов for?

Ответы

Ответ 1

То, что вы предложили, будет дробным изменением, но если вы умножаетесь на 100, вы получаете "процентное отклонение":

pv<- vector("numeric",length(x))
pv[1] <- 0
pv[-1] <- 100* ( x[-1] - x[-length(x)] )/ x[-length(x)]

Векторизованное решение. (И вы должны заметить, что for-loops будут такими же медленными, как * применять решения... просто не так красиво. Всегда ищите векторизованный подход.)

Чтобы объяснить немного больше: x[-length(x)] - это вектор, x[1:(length{x-1)], а x[-1] - вектор, x[2:length(x)], а векторные операции в R выполняют те же операции, что и в вашем for- тело цикла, хотя и не использует явный цикл. R сначала строит различия в сдвинутых векторах x[-length(x)] - x[-1], а затем делит на x[1:(length{x-1)].

Ответ 2

Вы можете получить те же результаты:

pv <- c(0)
y <- sapply(2:length(x), function(i) {pv <<- (x[i] - x[i-1])/x[i-1]})
c(0, y)

Проблемы с циклом, которые когда-то были проблемой, были оптимизированы. Часто цикл for не медленнее и может быть даже быстрее, чем применяемое решение. Вы должны проверить их обоих и посмотреть. Я ставлю, что ваш цикл for быстрее, чем мое решение.

EDIT: просто чтобы проиллюстрировать решение for loop или apply, а также то, что DWin обсуждает в области векторизации, я провел бенчмаркинг по четырем решениям, используя микрообъект на машине с выигрышем 7.

Unit: microseconds
             expr     min      lq  median      uq       max
1    DIFF_Vincent  22.396  25.195  27.061  29.860  2073.848
2        FOR.LOOP 132.037 137.168 139.968 144.634 56696.989
3          SAPPLY 146.033 152.099 155.365 162.363  2321.590
4 VECTORIZED_Dwin  18.196  20.063  21.463  23.328   536.075

enter image description here

Ответ 3

Вы также можете использовать diff:

c( 0, diff(x) / x[-length(x)] )
c( 0, exp(diff(log(x))) - 1 )