Найбільша проблема та неефективність - це індексація data.frame, я маю на увазі всі ці рядки, де ви користуєтесь temp[,]
.
Намагайтеся максимально уникати цього. Я взяв вашу функцію, змінив індексацію і ось версія_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
код для виконання умовного закінчення
якщо ви знаєте, що у ваших даних максимальна послідовність даних не велика, то ви можете змінити цикл на векторизований, а щось подібне
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Код, який використовується для моделювання та фігур, доступний на GitHub .