2015-10-26

Una manera más eficiente de comparar números que con ifelse en R

Title

Problema

Tenemos el siguiente data frame.

       x  y
[1,]  -1 99
[2,]   5  4
[3,]  10 -2
[4,] 600  0
[5,] -16  1
[6,]   0 55

Y deseamos obtener un vector fruto de la siguiente comparación de x e y:

1. El menor valor en y si tanto x e y son positivos.
2. Cualquier valor positivo de y si x es negativo.
3. O dejar el valor de x si no se cumplen las dos condiciones anteriores.

Datos

dat <- structure(list(x = c(-1L, 5L, 10L, 600L, -16L, 0L), y = c(99L, 
4L, -2L, 0L, 1L, 55L)), .Names = c("x", "y"), class = "data.frame", row.names = c("[1,]", 
"[2,]", "[3,]", "[4,]", "[5,]", "[6,]"))

Soluciones

 # Extraemos los vectores
x <- dat[, 1]
y <- dat[, 2]
  • ifelse
  • El problema de esta opción es que no es muy eficiente computacionalmente.

    ifelse(y >= 0, ifelse(x < 0, y, ifelse(x > y, y, x)), x)
    
     [1] 99  4 10  0  1  0

  • Indexando
  • Esta opción es entre 5 y 6 veces más rápida.

    xz[(x < y & x >= 0)| y < 0] <- x[(x < y & x >= 0)| y < 0];z
    
     [1] 99  4 10  0  1  0
  • Sin indexar
  • Aún más rápida que la anterior.

    x * ((x < y & x >= 0) | y < 0) + y * ((x > y & y >= 0) | x < 0) 
    
     [1] 99  4 10  0  1  0

    Comparativa

    Empleamos el paquete microbenchmark para comparar el tiempo de ejecución de las 3 opciones.

    microbenchmark(
      if_else = ifelse(y >= 0, ifelse(x < 0, y, ifelse(x > y, y, x)), x),
      indexar= z[(x < y & x >= 0)| y < 0] <- x[(x < y & x >= 0)| y < 0],  
      sinindexar = x *((x < y & x >= 0)| y < 0)+ y * ((x > y & y >= 0)| x < 0),
        )
    
    Unit: microseconds
           expr    min      lq     mean median     uq     max neval cld
        if_else 43.023 47.1785 53.29921 53.289 53.290 107.067   100  b
        indexar  8.800  9.2900 10.71218  9.778 10.267  53.290   100  a 
     sinindexar  7.333  7.8230  8.89833  8.311  8.800  52.800   100  a 
    
    La opción ifelse es la menos eficiente. La opción indexar mejora sustancialmente la velocidad de ejecución. Finalmente, la opción sinindexar es algo más rápida aún que indexar. Existen opciones más rápidas incluso, que puedes ver en el enlace del apartado referencias. Sin embargo, quizá la ganancia en velocidad de ejecución no compensa la pérdida de legibilidad.

    Entradas relacionadas

    Referencias

    No hay comentarios:

    Publicar un comentario

    Nube de datos