Самая большая проблема и корень неэффективности - это индексирование data.frame, я имею в виду все эти строки, где вы используете temp[,]
.
Постарайтесь избежать этого как можно больше. Я взял твою функцию, поменяй индексацию и вот version_A
dayloop2_A <- function(temp){
res <- numeric(nrow(temp))
for (i in 1:nrow(temp)){
res[i] <- i
if (i > 1) {
if ((temp[i,6] == temp[i-1,6]) & (temp[i,3] == temp[i-1,3])) {
res[i] <- temp[i,9] + res[i-1]
} else {
res[i] <- temp[i,9]
}
} else {
res[i] <- temp[i,9]
}
}
temp$`Kumm.` <- res
return(temp)
}
Как видите, я создаю вектор res
, который собирает результаты. В конце я добавляю его к data.frame
, и мне не нужно связываться с именами.
Так как же лучше?
Я запускаю каждую функцию для data.frame
с nrow
от 1000 до 10000 на 1000 и измеряю время с помощью system.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
Результат
Вы можете видеть, что ваша версия экспоненциально зависит от nrow(X)
. Модифицированная версия имеет линейную зависимость, а простая модель lm
предсказывает, что для 850 000 строк вычисление занимает 6 минут и 10 секунд.
Сила векторизации
Как Шейн и Калимо заявляют в своих ответах, векторизация - ключ к повышению производительности.
Из вашего кода вы можете выйти за пределы цикла:
- Кондиционер
- инициализация результатов (которые
temp[i,9]
)
Это приводит к этому коду
dayloop2_B <- function(temp){
cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
res <- temp[,9]
for (i in 1:nrow(temp)) {
if (cond[i]) res[i] <- temp[i,9] + res[i-1]
}
temp$`Kumm.` <- res
return(temp)
}
Сравните результат для этих функций, на этот раз для nrow
с 10 000 до 100 000 на 10 000.
Настройка настроенного
Другим изменением является изменение в цикле индексации temp[i,9]
на res[i]
(что точно так же в итерации i-го цикла).
Это опять разница между индексированием вектора и индексированием data.frame
.
Второе: когда вы смотрите на петлю, вы видите, что нет необходимости перебирать все i
, а только те, которые соответствуют условию.
Итак, мы идем
dayloop2_D <- function(temp){
cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
res <- temp[,9]
for (i in (1:nrow(temp))[cond]) {
res[i] <- res[i] + res[i-1]
}
temp$`Kumm.` <- res
return(temp)
}
Производительность, которую вы получаете, сильно зависит от структуры данных. Точно - на процент TRUE
значений в условии.
Для моих смоделированных данных требуется время вычисления на 850 000 строк ниже одной секунды.
Если вы хотите, вы можете пойти дальше, я вижу, по крайней мере, две вещи, которые можно сделать:
- написать код
C
, чтобы выполнить условное перечисление
если вы знаете, что в ваших данных максимальная последовательность не велика, вы можете изменить цикл на векторизованное время, что-то вроде
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Код, используемый для моделирования и рисунков: доступен на GitHub .