2012-04-24 12 views
6

Tengo un código y el tiempo total transcurrido es de alrededor de 30 segundos, el siguiente código es de alrededor de 27 segundos. Entrecerré el código infractor a esto:¿Por qué esto es tan lento? (bucle en una fila DF frente a un vector independiente)

d$dis300[i] <- h 

así que cambio a esta otra pieza y ahora está trabajando muy rápido (como se esperaba).

Mi pregunta es por qué esto es demasiado lento contra el segundo. El Datos DF es de alrededor de 7500x18 vars

Primera: (27 seg transcurrido)

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 
} 

Segundo: (0,2 seg transcurrido)

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 

se puede ver que ambos son la "misma", pero el Ofender uno tiene este DF en lugar de un solo vector.

Cualquier comentario es realmente apreciado. Vengo de otro tipo de idiomas y esto me volvió loco durante un tiempo. Al menos tengo una solución, pero me gustaría evitar este tipo de problemas en el futuro.

Gracias por su tiempo,

+0

Solo para aclarar, la diferencia de velocidad entre los dos es de 30 segundos frente a 27 segundos, y ¿considera esto una aceleración dramática? – joran

+0

Si los tiempos relativos de @ joran son correctos (¿y cuándo está equivocado? :-)), obtendrá mejores aceleraciones adoptando estos hábitos y técnicas: http://stackoverflow.com/questions/2908822/speed -up-the-loop-operation-in-r/8474941 # 8474941 –

+0

@ gsk3 Todos los días, según mi esposa. – joran

Respuesta

10

La razón es que d$dis300[i] <- h llama $<-.data.frame.

Es una función bastante compleja como se puede ver:

`$<-.data.frame` 

Usted no dice lo que es foo, pero si es un vector atómica, la función $<- se implementa en C para la velocidad.

Aún así, espero que se declara foo de la siguiente manera:

foo <- numeric(netot) 

Esto asegurará que no es necesario reasignar el vector para cada asignación en el bucle:

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 

Utilizando el *apply familia en su lugar no se preocupe por eso:

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) 

... aquí también ext resistió d$ent y d$dis fuera del circuito, lo que también debería mejorar las cosas. No puedo ejecutarlo aunque no haya dado datos reproducibles. Pero aquí hay un ejemplo similar:

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 

... pero finalmente parece que todo puede ser reducido a (excluyendo el código de detección de errores):

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

+3 internets para ver el one-liner escondido en el morass. –

+0

+10 cervezas (y cervezas >> internets) –

+0

@JoshuaUlrich - ¿Puedo cambiarlos por una docena más de votos, así que alcanzo 10k; -) ... – Tommy

2

Tommy es la mejor respuesta. Esto era demasiado grande para hacer comentarios y agregarlo como respuesta ...

Así es como se puede ver las copias (de la totalidad de DF, como se comentó Joran) a sí mismo:

> 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 

Cada una de estas líneas tracemem[] corresponde a una copia de todo el objeto. Puede ver las direcciones hexadecimales del vector de columna a cambiando cada vez, también, a pesar de que no se modificó por la asignación a b.

yo sepa, sin que se caiga en código C a sí mismo, la única manera (en la actualidad) en R para modificar un artículo de un data.frame sin copia de cualquier memoria en absoluto, es el := operador y set() función, tanto en el paquete data.table. Hay 17 questions sobre la asignación por referencia usando := aquí en Desbordamiento de pila.

Pero en este caso, el único forro de Tommy es definitivamente mejor ya que ni siquiera necesitas un lazo.

+0

Gracias por esta lección Matthew. Atentamente. – notuo

Cuestiones relacionadas