Петли в 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 )