Como una cuestión de buenas 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, el bucle de una matriz a través de la función. He probado de las dos maneras y se sorprendió al encontrar apply() es más lento. La tarea es tomar un vector y evaluar como positivo o negativo y, a continuación, devolver un vector con 1 si es positivo y -1 si es negativo. El mash() función de bucles y el squish() función se pasa a la apply() función.

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

loop_million resultados:

user  system elapsed 
0.468   0.008   0.483 

apply_million resultados:

user  system elapsed 
1.401   0.021   1.423 

¿Cuál es la ventaja del uso de apply() más de un for bucle si se degrada el rendimiento? Hay un error en mi prueba? He comparado los dos objetos resultantes de una pista y se encontró:

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

Que sólo profundiza el misterio. El apply() función no puede aceptar una simple numérico del vector y es por eso que me lances con as.matrix() en el principio. Pero, a continuación, devuelve un valor numérico. El for bucle es buena, con un simple vector numérico. Y devuelve un objeto de la misma clase que uno ha pasado.

  • Uso system.time() en lugar de proc.time, es más adecuado para la tarea. O mejor aún, siga algunos de los ejemplos de este post y obtener mejores resultados mediante la replicación de la prueba varias veces y tomando la media de los que: stats.stackexchange.com/questions/3235/timing-functions-in-r
  • Gracias por el tiempo de enlace. Acaba de empezar en el benchmarking.
  • También debe comprobar microbenchmark paquete de medidas más exactas.
InformationsquelleAutor Milktrader | 2011-04-03

5 Comentarios

  1. 12

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

    A aclarar por qué su aplique la solución es más lento:

    Dentro del bucle for, en realidad el uso del vectorizados índices de la matriz, significa que no hay conversión de tipo pasando. Voy un poco áspera por aquí, pero básicamente el cálculo interno tipo de ignora las dimensiones. Son sólo mantuvo como un atributo y regresó con el vector de 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 se utiliza la aplicación, la matriz se divide internamente en 100.000 vectores fila, cada vector fila (es decir, un único número) se coloca a través de la función, y al final, el resultado se combina en una forma apropiada. Al aplicar la función calcula un vector es mejor en este caso, y por lo tanto tiene que concatenar los resultados de todas las filas. Esto toma tiempo.

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

    SI se aplican sería una solución aquí (y no es), se 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
    • usted espera que su comparación con letras mayúsculas, SI y ese punto no está perdido en mí. Necesito informe a pesar de que si me las aplicaciones de la muestra a 10 millones, el bucle es de 2 segundos más rápido que sapply pruebas. Claramente ifelse es el mejor, pero bucle todavía parece que el golpe fuera incorporado en aplicar funciones. Si yo tengo un problema diferente que ifelse() no sabe manejar, me temo que puedan favorecer la temida bucle en aplicar. Al menos no lo voy a tomar en la fe de que aplicar será mejor y lo más probable es que la prueba de la mejor solución.
    • gracias a ambas por el sistema.tiempo() y todo.igual() herramientas.
    • Con los vectores, se convierte en un asunto de interial diseño. Esto se puede ver en la diferencia de tiempos entre mis pruebas y Persecuciones así. Ahora, tenga en cuenta que hay otras razones para optar por aplicar. Chase le dio el enlace ya en los comentarios. También echa un vistazo a la diferencia entre aplicar, sapply, lapply y amigos, y la velocidad con opciones como el USO.NOMBRES=F y simplificar=F en sapply.
  2. 39

    El punto de aplicar (y plyr) de la familia de funciones no es de velocidad, sino de la expresividad. También tienden a evitar errores, ya que eliminan el libro de mantenimiento de código necesario con bucles.

    Últimamente, las respuestas en stackoverflow tienen más de relieve la velocidad. Tu código va a llegar más rápido a sus propias computadoras más y R-core optimiza el funcionamiento interno de R. el código nunca van a llegar más elegante o más fácil de entender por su propia cuenta.

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

    • esto se hace eco de lo que he encontrado en R Infierno por Quemaduras, que la aplican de la familia de funciones son básicamente los bucles de R y su beneficio no es de velocidad. Él lo llama bucle de la clandestinidad.
    • Me gustaría señalar que esta solución (que debe ser el predeterminado para tener en cuenta en este y otros casos similares), no sólo es muy rápido, es diez veces más rápido que ifelse, once veces más rápido que la OP del mash (utilizando for) y 162 veces más rápido que apply‘ción de la squish función de OP. (Tiempos de uso de library(microbenchmark) con times=100 y la million de OP como de datos.)
  3. 6

    Puede utilizar lapply o sapply en vectores, si quieres. Sin embargo, ¿por qué no utilizar 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 la comparación del bien, aquí están las dos comparables se ejecuta utilizando 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 
    • sapply uso es claramente superior en este ejemplo, pero el lazo es todavía más rápido. Por supuesto, no hay competencia cuando ifelse participa. Puede que no tenga mis términos correctos, pero no la aplican de la familia de funciones que son consideradas funciones de asignación, y me estoy imaginando que he leído que la asignación de funciones son preferibles a los bucles for en R?
    • +1 para la vectorización
    • ¿donde hay vectorización en @Chase respuesta? Es un concepto al que no he captado y viene un montón.
    • La función ifelse trabaja en un vector utilizando los bucles internos en R. Este no es el mismo que el bucle for o cualquiera de aplicar la función. ifelse() toma un vector, por lo que no hay necesidad de utilizar una explícita la función de bucle. Por lo tanto, ifelse es un vectorizados función.
    • buena info en aplicar vs bucles en R: stackoverflow.com/questions/2275896/…
  4. 4

    Que es mucho más rápido en este caso, basadas en el índice de reemplazo de la ifelse(), el *apply() de la familia, 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

    Es bien vale la pena tener todas estas herramientas en sus extremidades del dedo. Usted puede utilizar el que tenga más sentido para usted (como usted necesita entender el código de meses o años más tarde) y, a continuación, empezar a mover más soluciones optimizadas si el tiempo de cálculo se convierte en prohibitivo.

    • O de forma más sucinta, e incluso más rápido, (million > 0) * 2 - 1.
    • gracias por la comparación. Yo estoy entendiendo que ifelse() y la indización son vectorizations, o el uso de C hacer ejecutar el bucle. Todas las operaciones vectoriales de uso de bucles, pero si se pasa el trabajo a C, se obtiene más rápido. La explícita bucle y aplicar familia de funciones son similares debido a que se ejecute el bucle desde dentro R.
  5. 3

    Mejor ejemplo de la ventaja de velocidad de bucle for.

    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 
    • +1 para preallocation.
    • si utiliza máx.col (una función de base) que usted puede cortar a la mitad del tiempo.
    • He añadido algunos ejemplos. El tiempo puede ser inferior a 1/20 o similar a la del bucle for.
    • así, una «mejor ejemplo» la utilización de bucles perdido a vectorización (y de código C) … de nuevo.

Dejar respuesta

Please enter your comment!
Please enter your name here