Masalah terbesar dan akar dari ketidakefektifan adalah pengindeksan data.frame, maksud saya semua baris ini di mana Anda menggunakan temp[,]
.
Cobalah untuk menghindari ini sebanyak mungkin. Saya mengambil fungsi Anda, mengubah pengindeksan dan di sini 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)
}
Seperti yang Anda lihat, saya membuat vektor res
yang mengumpulkan hasil. Pada akhirnya saya menambahkannya data.frame
dan saya tidak perlu dipusingkan dengan nama. Jadi seberapa baik itu?
Saya menjalankan setiap fungsi data.frame
dengan nrow
dari 1.000 hingga 10.000 oleh 1.000 dan mengukur waktu dengansystem.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
Hasilnya adalah
Anda dapat melihat bahwa versi Anda bergantung secara eksponensial nrow(X)
. Versi modifikasi memiliki hubungan linier, dan lm
model sederhana memprediksi bahwa untuk 850.000 baris perhitungan membutuhkan waktu 6 menit dan 10 detik.
Kekuatan vektorisasi
Seperti yang dinyatakan Shane dan Calimo dalam jawaban mereka, vektorisasi adalah kunci kinerja yang lebih baik. Dari kode Anda, Anda bisa bergerak di luar lingkaran:
- pengkondisian
- inisialisasi hasil (yang
temp[i,9]
)
Ini mengarah ke kode ini
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)
}
Bandingkan hasil untuk fungsi ini, kali ini nrow
dari 10.000 hingga 100.000 dengan 10.000.
Tuning yang disetel
Tweak lain adalah mengubah pengindeksan loop temp[i,9]
ke res[i]
(yang persis sama di iterasi loop ke-i). Lagi-lagi perbedaan antara pengindeksan vektor dan pengindeksan a data.frame
.
Hal kedua: ketika Anda melihat loop Anda dapat melihat bahwa tidak perlu untuk mengulang semua i
, tetapi hanya untuk orang-orang yang sesuai dengan kondisi.
Jadi di sini kita mulai
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)
}
Kinerja yang Anda peroleh sangat tergantung pada struktur data. Tepatnya - pada persen TRUE
nilai dalam kondisi. Untuk data simulasi saya, dibutuhkan waktu komputasi untuk 850.000 baris di bawah satu detik.
Saya ingin Anda dapat melangkah lebih jauh, saya melihat setidaknya dua hal yang dapat dilakukan:
- menulis sebuah
C
kode untuk melakukan cumsum kondisional
jika Anda tahu bahwa dalam urutan max data Anda tidak besar maka Anda dapat mengubah loop ke vektor sementara, sesuatu seperti
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Kode yang digunakan untuk simulasi dan angka tersedia di GitHub .