2012-04-24 9 views
6

Ho un pezzo di codice e il tempo totale trascorso è di circa 30 secondi, il codice seguente è di circa 27 secondi. Ho ridotto il codice incriminato per questo:Perché è così lento? (loop in una riga DF rispetto a un vettore standalone)

d$dis300[i] <- h 

Così posso cambiare a questo altro pezzo e sta attualmente lavorando veramente veloce (come previsto).

La mia domanda è perché questo è troppo lento rispetto al secondo. Il datos DF è di circa 7500x18 vars

Primo: (27 sec trascorsi)

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 
} 

Secondo: (0,2 sec trascorso)

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 

è possibile vedere entrambi sono la "stessa", ma la offendendo uno ha questo DF invece di un singolo vettore.

Qualsiasi commento è molto apprezzato. Sono venuto da un altro tipo di lingue e questo mi ha fatto impazzire per un po '. Almeno ho una soluzione, ma mi piace prevenire questo tipo di problemi in futuro.

Grazie per il vostro tempo,

+0

Giusto per chiarire, la differenza di velocità tra i due è di 30 secondi contro 27 secondi, e si considera una drammatica accelerazione? – joran

+0

Se i tempi relativi di @ joran sono corretti (e quando si sbagliano? :-)), otterrete dei miglioramenti molto migliori adottando queste abitudini e tecniche: http://stackoverflow.com/questions/2908822/speed -up-the-loop-operation-in-r/8474941 # 8474941 –

+0

@ gsk3 Daily, secondo mia moglie. – joran

risposta

10

La ragione è che d$dis300[i] <- h chiama $<-.data.frame.

E 'una funzione piuttosto complessa, come potete vedere:

`$<-.data.frame` 

Tu non dici quello che foo è, ma se si tratta di un vettore atomica, la funzione $<- è implementato in C per la velocità.

Ancora, spero si dichiara foo come segue:

foo <- numeric(netot) 

Questo farà sì che non è necessario riallocare il vettore per ogni incarico nel loop:

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 

Utilizzando la *apply famiglia invece non ti preoccupare:

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) 

... qui ho anche ext ratto d$ent e d$dis fuori dal circuito che dovrebbe migliorare un po 'anche le cose. Non riesco a eseguirlo da me, visto che non hai dato dati riproducibili. Ma qui è un esempio simile:

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 

... ma alla fine sembra che tutto può essere ridotto a (salvo il codice di rilevamento degli errori):

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

+3 internets per vedere l'one-liner nascosto nella palude. –

+0

+10 birre (e birre >> internet) –

+0

@JoshuaUlrich - Posso scambiarli con una dozzina di voti in più, quindi raggiungo 10k; -) ... – Tommy

2

Tommy è la migliore risposta. Era troppo grande per un commento, quindi aggiungendolo come risposta ...

In questo modo si possono vedere le copie (di tutta la DF, come Joran commentato) te stesso:

> 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 

Ognuno di questi tracemem[] linee corrisponde a una copia l'intero oggetto. È possibile visualizzare gli indirizzi esadecimali del vettore di colonna a che cambiano ogni volta, nonostante non vengano modificati dall'assegnazione a b.

AFAIK, senza cadere in codice C te, l'unico modo (attualmente) in R per modificare un elemento di un data.frame senza copia di qualsiasi memoria a tutti, è il := operatore e set() funzione, sia in pacchetto data.table. Ci sono 17 questions sull'assegnazione per riferimento utilizzando := qui su Stack Overflow.

Ma in questo caso il rivestimento di Tommy è sicuramente il migliore in quanto non è nemmeno necessario un ciclo.

+0

Grazie per questa lezione Matteo. I migliori saluti. – notuo

Problemi correlati