2012-04-24 37 views
6

Tôi có một đoạn mã và tổng thời gian trôi qua là khoảng 30 giây, mã sau đây là khoảng 27 giây. Tôi đã thu hẹp mã vi phạm này:Tại sao điều này quá chậm? (vòng lặp trong hàng DF so với vectơ độc lập)

d$dis300[i] <- h 

Vì vậy, tôi thay đổi thành phần khác này và hiện đang làm việc rất nhanh (như mong đợi).

Câu hỏi của tôi là tại sao điều này quá chậm so với giây. Các DATOS DF là khoảng 7500x18 vars

đầu tiên: (27 giây trôi qua)

d$dis300 <- 0 
for (i in 1:netot) { 
    h <- aaa[d$ent[i], d$dis[i]] 
    if (h == 0) writeLines(sprintf("ERROR. ent:%i dis:%i", d$ent[i], d$dis[i])) 
    d$dis300[i] <- h 
} 

Thứ hai: (0,2 giây trôi qua)

d$dis300 <- 0 
for (i in 1:netot) { 
    h <- aaa[d$ent[i], d$dis[i]] 
    if (h == 0) writeLines(sprintf("ERROR. ent:%i dis:%i", d$ent[i], d$dis[i])) 
    foo[i] <- h 
} 
d$foo <- foo 

Bạn có thể thấy cả hai đều là những "tương tự", nhưng vi phạm một trong những có DF này thay vì một vector duy nhất.

Bất kỳ nhận xét nào thực sự được đánh giá cao. Tôi đến từ một loại ngôn ngữ khác và điều này đã khiến tôi mất nhiều thời gian. Ít nhất tôi có giải pháp nhưng tôi muốn ngăn chặn loại vấn đề này trong tương lai.

Cảm ơn thời gian của bạn,

+0

Chỉ cần làm rõ, sự khác biệt về tốc độ giữa hai là 30 giây so với 27 giây, và bạn xem đây là một tốc độ đáng kể? – joran

+0

Nếu thời gian tương đối của @ joran là chính xác (và khi nào anh ấy sai? :-)), bạn sẽ nhận được rất nhiều tốc độ tốt hơn bằng cách áp dụng những thói quen và kỹ thuật sau: http://stackoverflow.com/questions/2908822/speed -up-the-loop-operation-in-r/8474941 # 8474941 –

+0

@ gsk3 Hàng ngày, theo vợ tôi. – joran

Trả lời

10

Lý do là d$dis300[i] <- h gọi $<-.data.frame.

Đó là một chức năng khá phức tạp như bạn có thể thấy:

`$<-.data.frame` 

Bạn không nói foo là gì, nhưng nếu nó là một véc tơ nguyên tử, các $<- hàm được thực hiện trong C cho tốc độ.

Tuy nhiên, tôi hy vọng bạn khai báo foo như sau:

foo <- numeric(netot) 

Điều này sẽ đảm bảo bạn không cần phải phân bổ lại các vector cho mỗi bài tập trong vòng lặp:

foo <- 0 # BAD! 
system.time(for(i in 1:5e4) foo[i] <- 0) # 4.40 secs 
foo <- numeric(5e4) # Pre-allocate 
system.time(for(i in 1:5e4) foo[i] <- 0) # 0.09 secs 

Sử dụng *apply gia đình thay vì bạn không lo lắng về điều đó:

d$foo <- vapply(1:netot, function(i, aaa, ent, dis) { 
    h <- aaa[ent[i], dis[i]] 
    if (h == 0) writeLines(sprintf("ERROR. ent:%i dis:%i", ent[i], dis[i])) 
    h 
}, numeric(1), aaa=aaa, ent=d$ent, dis=d$dis) 

... tại đây tôi cũng ext racted d$entd$dis bên ngoài vòng lặp nên cải thiện mọi thứ một chút. Không thể tự chạy mặc dù bạn không cung cấp dữ liệu có thể tái sản xuất. Nhưng đây là một ví dụ tương tự:

d <- data.frame(x=1) 
system.time(vapply(1:1e6, function(i) d$x, numeric(1)))   # 3.20 secs 
system.time(vapply(1:1e6, function(i, x) x, numeric(1), x=d$x)) # 0.56 secs 

... nhưng cuối cùng có vẻ như nó đều có thể được giảm xuống (trừ đang phát hiện lỗi của bạn):

d$foo <- aaa[cbind(d$ent, d$dis)] 
+4

+3 internets để xem một lớp lót ẩn trong hành vi. –

+0

+10 bia (và bia >> internets) –

+0

@JoshuaUlrich - Tôi có thể đổi chúng cho hơn một chục phiếu bầu khác không vì vậy tôi đạt 10k; -) ... – Tommy

2

của Tommy là câu trả lời tốt nhất. Điều này quá lớn để nhận xét, vì vậy hãy thêm nó làm câu trả lời ...

Đây là cách bạn sẽ nhìn thấy bản sao (của toàn bộ DF, như Joran nhận xét) mình:

> DF = data.frame(a=1:3,b=4:6) 
> tracemem(DF) 
[1] "<0x0000000003104800" 
> for (i in 1:3) {DF$b[i] <- i; .Internal(inspect(DF))} 
tracemem[0000000003104800 -> 000000000396EAD8]: 
tracemem[000000000396EAD8 -> 000000000396E4F0]: $<-.data.frame $<- 
tracemem[000000000396E4F0 -> 000000000399CDC8]: $<-.data.frame $<- 
@000000000399CDC8 19 VECSXP g0c2 [OBJ,NAM(2),TR,ATT] (len=2, tl=0) 
    @000000000399CD90 13 INTSXP g0c2 [] (len=3, tl=0) 1,2,3 
    @000000000399CCE8 13 INTSXP g0c2 [] (len=3, tl=0) 1,5,6 
ATTRIB: # .. snip .. 

tracemem[000000000399CDC8 -> 000000000399CC40]: 
tracemem[000000000399CC40 -> 000000000399CAB8]: $<-.data.frame $<- 
tracemem[000000000399CAB8 -> 000000000399C9A0]: $<-.data.frame $<- 
@000000000399C9A0 19 VECSXP g0c2 [OBJ,NAM(2),TR,ATT] (len=2, tl=0) 
    @000000000399C968 13 INTSXP g0c2 [] (len=3, tl=0) 1,2,3 
    @000000000399C888 13 INTSXP g0c2 [] (len=3, tl=0) 1,2,6 
ATTRIB: # .. snip .. 

tracemem[000000000399C9A0 -> 000000000399C7E0]: 
tracemem[000000000399C7E0 -> 000000000399C700]: $<-.data.frame $<- 
tracemem[000000000399C700 -> 00000000039C78D8]: $<-.data.frame $<- 
@00000000039C78D8 19 VECSXP g0c2 [OBJ,NAM(2),TR,ATT] (len=2, tl=0) 
    @00000000039C78A0 13 INTSXP g0c2 [] (len=3, tl=0) 1,2,3 
    @0000000003E07890 13 INTSXP g0c2 [] (len=3, tl=0) 1,2,3 
ATTRIB: # .. snip .. 
> DF 
    a b 
1 1 1 
2 2 2 
3 3 3 

Mỗi của những tracemem[] dòng tương ứng với một bản sao của toàn bộ đối tượng. Bạn cũng có thể thấy các địa chỉ thập phân của vectơ a cột thay đổi mỗi lần, mặc dù nó không được sửa đổi bởi việc gán cho b.

AFAIK, mà không rơi vào mã C cho mình, cách duy nhất (hiện tại) trong R để sửa đổi một mục của một data.frame không có bản sao của bất kỳ bộ nhớ ở tất cả, là := điều hành và set() chức năng, cả hai trong gói data.table. Có 17 questions về việc chỉ định bằng tham chiếu bằng cách sử dụng := tại đây trên Stack Overflow.

Nhưng trong trường hợp này, một chiếc lót của Tommy chắc chắn là tốt nhất vì bạn thậm chí không cần một vòng lặp nào cả.

+0

Cảm ơn vì bài học này Matthew. Trân trọng. – notuo

Các vấn đề liên quan