Самая большая проблема и корень неэффективности заключается в индексации 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-го цикла). Это опять разница между индексированием вектора и индексированием a 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
код, чтобы сделать условное cumsum
если вы знаете, что в ваших данных максимальная последовательность не велика, вы можете изменить цикл на векторизованное время, что-то вроде
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Код, используемый для моделирования и рисунков, доступен на GitHub .