while studio funciones for estructuras estructura ejercicios ejemplos control contador condicionales r benchmarking

studio - funciones r en r ejemplos



¿Por qué el método apply() es más lento que un bucle for en R? (5)

Como dijo Chase: Usa el poder de la vectorización. Estás comparando dos soluciones malas aquí.

Para aclarar por qué su solución de aplicación es más lenta:

Dentro del bucle for, realmente utilizas los índices vectorizados de la matriz, lo que significa que no hay conversión de tipo en curso. Lo estoy haciendo un poco duro aquí, pero básicamente el tipo de cálculo interno ignora las dimensiones. Simplemente se mantienen como un atributo y se devuelven con el vector que representa la matriz. Para ilustrar :

> x <- 1:10 > attr(x,"dim") <- c(5,2) > y <- matrix(1:10,ncol=2) > all.equal(x,y) [1] TRUE

Ahora, cuando utiliza la aplicación, la matriz se divide internamente en 100,000 vectores de fila, cada vector de fila (es decir, un solo número) se pone a través de la función, y al final el resultado se combina en una forma apropiada. La función de aplicación considera que un vector es mejor en este caso, y por lo tanto tiene que concatenar los resultados de todas las filas. Esto lleva tiempo.

Además, la función sapply primero usa as.vector(unlist(...)) para convertir cualquier cosa en un vector, y al final intenta simplificar la respuesta en una forma adecuada. También esto lleva tiempo, por lo tanto, también podría ser más lento el suministro aquí. Sin embargo, no está en mi máquina.

SI aplicar sería una solución aquí (y no lo es), podría comparar:

> system.time(loop_million <- mash(million)) user system elapsed 0.75 0.00 0.75 > system.time(sapply_million <- matrix(unlist(sapply(million,squish,simplify=F)))) user system elapsed 0.25 0.00 0.25 > system.time(sapply2_million <- matrix(sapply(million,squish))) user system elapsed 0.34 0.00 0.34 > all.equal(loop_million,sapply_million) [1] TRUE > all.equal(loop_million,sapply2_million) [1] TRUE

Como cuestión de mejores prácticas, estoy tratando de determinar si es mejor crear una función y apply() a través de una matriz, o si es mejor simplemente pasar una matriz a través de la función. Lo intenté de ambas maneras y me sorprendió encontrar que apply() es más lento. La tarea es tomar un vector y evaluarlo como positivo o negativo y luego devolver un vector con 1 si es positivo y -1 si es negativo. La función mash() bucle y la función squish() pasa a la función apply() .

million <- as.matrix(rnorm(100000)) mash <- function(x){ for(i in 1:NROW(x)) if(x[i] > 0) x[i] <- 1 else x[i] <- -1 return(x) } squish <- function(x){ if(x >0) return(1) else return(-1) } ptm <- proc.time() loop_million <- mash(million) proc.time() - ptm ptm <- proc.time() apply_million <- apply(million,1, squish) proc.time() - ptm

resultados loop_million :

user system elapsed 0.468 0.008 0.483

Resultados de apply_million :

user system elapsed 1.401 0.021 1.423

¿Cuál es la ventaja de usar apply() en un bucle for si el rendimiento está degradado? ¿Hay algún fallo en mi prueba? Comparé los dos objetos resultantes para una pista y encontré:

> class(apply_million) [1] "numeric" > class(loop_million) [1] "matrix"

Lo que sólo profundiza el misterio. La función apply() no puede aceptar un vector numérico simple y es por eso que lo as.matrix() con as.matrix() al principio. Pero luego devuelve un numérico. El bucle for está bien con un simple vector numérico. Y devuelve un objeto de la misma clase que el que le pasó.


El punto de la familia de funciones apply (y plyr) no es la velocidad, sino la expresividad. También tienden a prevenir errores porque eliminan el código de mantenimiento de libros necesario con los bucles.

Últimamente, las respuestas en han sobreestimado la velocidad. Su código se volverá más rápido por sí mismo a medida que las computadoras se vuelvan más rápidas y R-core optimice las partes internas de R. Su código nunca será más elegante ni más fácil de entender por sí solo.

En este caso, puede tener lo mejor de ambos mundos: una respuesta elegante con vectorización que también es muy rápida, (million > 0) * 2 - 1 .


En este caso, es mucho más rápido hacer una sustitución basada en índices que la familia ifelse() , la familia *apply() o el bucle:

> million <- million2 <- as.matrix(rnorm(100000)) > system.time(million3 <- ifelse(million > 0, 1, -1)) user system elapsed 0.046 0.000 0.044 > system.time({million2[(want <- million2 > 0)] <- 1; million2[!want] <- -1}) user system elapsed 0.006 0.000 0.007 > all.equal(million2, million3) [1] TRUE

Vale la pena tener todas estas herramientas a tu alcance. Puede usar el que tenga más sentido para usted (ya que necesita entender el código meses o años más tarde) y luego comenzar a buscar soluciones más optimizadas si el tiempo de cálculo se vuelve prohibitivo.


Mejor ejemplo de ventaja de velocidad de bucle.

for_loop <- function(x){ out <- vector(mode="numeric",length=NROW(x)) for(i in seq(length(out))) out[i] <- max(x[i,]) return(out) } apply_loop <- function(x){ apply(x,1,max) } million <- matrix(rnorm(1000000),ncol=10) > system.time(apply_loop(million)) user system elapsed 0.57 0.00 0.56 > system.time(for_loop(million)) user system elapsed 0.32 0.00 0.33

EDITAR

Versión sugerida por eduardo.

max_col <- function(x){ x[cbind(seq(NROW(x)),max.col(x))] }

Por fila

> system.time(for_loop(million)) user system elapsed 0.99 0.00 1.11 > system.time(apply_loop(million)) user system elapsed 1.40 0.00 1.44 > system.time(max_col(million)) user system elapsed 0.06 0.00 0.06

Por columna

> system.time(for_loop(t(million))) user system elapsed 0.05 0.00 0.05 > system.time(apply_loop(t(million))) user system elapsed 0.07 0.00 0.07 > system.time(max_col(t(million))) user system elapsed 0.04 0.00 0.06


Puedes usar lapply o sapply en vectores si quieres. Sin embargo, ¿por qué no usar la herramienta adecuada para el trabajo, en este caso ifelse() ?

> ptm <- proc.time() > ifelse_million <- ifelse(million > 0,1,-1) > proc.time() - ptm user system elapsed 0.077 0.007 0.093 > all.equal(ifelse_million, loop_million) [1] TRUE

Y para fines de comparación, aquí están las dos ejecuciones comparables que usan el bucle for y sapply:

> ptm <- proc.time() > apply_million <- sapply(million, squish) > proc.time() - ptm user system elapsed 0.469 0.004 0.474 > ptm <- proc.time() > loop_million <- mash(million) > proc.time() - ptm user system elapsed 0.408 0.001 0.417