Vấn đề lớn nhất và gốc rễ của sự kém hiệu quả là lập chỉ mục data.frame, ý tôi là tất cả các dòng này nơi bạn sử dụng temp[,]
.
Cố gắng tránh điều này càng nhiều càng tốt. Tôi đã lấy chức năng của bạn, thay đổi chỉ mục và ở đây phiên bản_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)
}
Như bạn có thể thấy tôi tạo vector res
thu thập kết quả. Cuối cùng, tôi thêm nó vào data.frame
và tôi không cần phải lộn xộn với tên. Vậy làm thế nào tốt hơn?
Tôi chạy từng chức năng data.frame
với nrow
từ 1.000 đến 10.000 bằng 1.000 và đo thời gian bằngsystem.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
Kết quả là
Bạn có thể thấy rằng phiên bản của bạn phụ thuộc theo cấp số nhân nrow(X)
. Phiên bản sửa đổi có mối quan hệ tuyến tính và lm
mô hình đơn giản dự đoán rằng để tính toán 850.000 hàng mất 6 phút và 10 giây.
Sức mạnh của vector hóa
Như Shane và Calimo nói trong câu trả lời của họ, vector hóa là chìa khóa để thực hiện tốt hơn. Từ mã của bạn, bạn có thể di chuyển ra ngoài vòng lặp:
- điều hòa
- khởi tạo kết quả (đó là
temp[i,9]
)
Điều này dẫn đến mã này
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)
}
So sánh kết quả cho các chức năng này, lần này cho nrow
từ 10.000 đến 100.000 bằng 10.000.
Điều chỉnh điều chỉnh
Một điều chỉnh khác là thay đổi chỉ mục vòng lặp temp[i,9]
thành res[i]
(chính xác là giống nhau trong lần lặp vòng lặp thứ i). Một lần nữa, sự khác biệt giữa lập chỉ mục một vectơ và lập chỉ mục a data.frame
.
Điều thứ hai: khi bạn nhìn vào vòng lặp, bạn có thể thấy rằng không cần phải lặp lại tất cả i
, mà chỉ cho những điều kiện phù hợp.
Vì vậy, ở đây chúng tôi đ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)
}
Hiệu suất mà bạn đạt được phụ thuộc nhiều vào cấu trúc dữ liệu. Chính xác - trên phần trăm của các TRUE
giá trị trong điều kiện. Đối với dữ liệu mô phỏng của tôi, phải mất thời gian tính toán cho 850.000 hàng dưới một giây.
Tôi muốn bạn có thể đi xa hơn, tôi thấy ít nhất hai điều có thể được thực hiện:
- viết
C
mã để làm cumsum có điều kiện
nếu bạn biết rằng trong chuỗi dữ liệu tối đa của bạn không lớn thì bạn có thể thay đổi vòng lặp thành vectơ trong khi, đại loại như
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Mã được sử dụng cho mô phỏng và số liệu có sẵn trên GitHub .