varias superponer studio lineas histogramas graficos graficas r coalesce

studio - superponer graficas en r



Cómo implementar la fusión de manera eficiente en R (7)

Fondo

Varios lenguajes SQL (utilizo principalmente postgreSQL) tienen una función llamada coalesce que devuelve el primer elemento de columna no nulo para cada fila. Esto puede ser muy eficiente de usar cuando las tablas tienen muchos elementos NULL en ellos.

Me encuentro con esto en muchos escenarios en R también cuando trato con datos no tan estructurados que tienen muchas NA en ellos.

He realizado una implementación ingenua pero es ridículamente lenta.

coalesce <- function(...) { apply(cbind(...), 1, function(x) { x[which(!is.na(x))[1]] }) }

Ejemplo

a <- c(1, 2, NA, 4, NA) b <- c(NA, NA, NA, 5, 6) c <- c(7, 8, NA, 9, 10) coalesce(a,b,c) # [1] 1 2 NA 4 6

Pregunta

¿Hay alguna manera eficiente de implementar la coalesce en R?


Aquí está mi solución:

coalesce <- function(x){ y <- head( x[is.na(x) == F] , 1) return(y) } Devuelve el primer vaule que no es NA y funciona en data.table , para ejemplo si desea usar coalesce en pocas columnas y estos nombres de columna están en el vector de cadenas:

column_names <- c("col1", "col2", "col3")

cómo utilizar:

ranking[, coalesce_column := coalesce( mget(column_names) ), by = 1:nrow(ranking)]


En mi máquina, al usar Reduce obtiene una mejora de rendimiento de 5x:

coalesce2 <- function(...) { Reduce(function(x, y) { i <- which(is.na(x)) x[i] <- y[i] x}, list(...)) } > microbenchmark(coalesce(a,b,c),coalesce2(a,b,c)) Unit: microseconds expr min lq median uq max neval coalesce(a, b, c) 97.669 100.7950 102.0120 103.0505 243.438 100 coalesce2(a, b, c) 19.601 21.4055 22.8835 23.8315 45.419 100


Otro método de aplicación, con mapply .

mapply(function(...) {temp <- c(...); temp[!is.na(temp)][1]}, a, b, c) [1] 1 2 NA 4 6

Esto selecciona el primer valor distinto de NA si existe más de uno. El último elemento no perdido se puede seleccionar usando tail .

Tal vez un poco más de velocidad podría ser exprimido de esta alternativa usando la .mapply función .mapply , que se ve un poco diferente.

unlist(.mapply(function(...) {temp <- c(...); temp[!is.na(temp)][1]}, dots=list(a, b, c), MoreArgs=NULL)) [1] 1 2 NA 4 6

.mapply difiere de manera importante de su primo no punteado.

  • devuelve una lista (como Map ) y por lo tanto debe estar envuelto en alguna función como unlist o c para devolver un vector.
  • el conjunto de argumentos que se debe alimentar en paralelo a la función en FUN se debe dar en una lista al argumento de puntos.
  • Finalmente, mapply , el argumento moreArgs no tiene un valor predeterminado, por lo que debe alimentarse explícitamente con NULL.

Parece que coalesce1 todavía está disponible

coalesce1 <- function(...) { ans <- ..1 for (elt in list(...)[-1]) { i <- is.na(ans) ans[i] <- elt[i] } ans }

que es aún más rápido (pero más o menos una reescritura de la mano de Reduce , por lo tanto, menos general)

> identical(coalesce(a, b, c), coalesce1(a, b, c)) [1] TRUE > microbenchmark(coalesce(a,b,c), coalesce1(a, b, c), coalesce2(a,b,c)) Unit: microseconds expr min lq median uq max neval coalesce(a, b, c) 336.266 341.6385 344.7320 355.4935 538.348 100 coalesce1(a, b, c) 8.287 9.4110 10.9515 12.1295 20.940 100 coalesce2(a, b, c) 37.711 40.1615 42.0885 45.1705 67.258 100

O para comparar datos más grandes

coalesce1a <- function(...) { ans <- ..1 for (elt in list(...)[-1]) { i <- which(is.na(ans)) ans[i] <- elt[i] } ans }

mostrando aquello which() veces puede ser efectivo, aunque implique un segundo pase a través del índice.

> aa <- sample(a, 100000, TRUE) > bb <- sample(b, 100000, TRUE) > cc <- sample(c, 100000, TRUE) > microbenchmark(coalesce1(aa, bb, cc), + coalesce1a(aa, bb, cc), + coalesce2(aa,bb,cc), times=10) Unit: milliseconds expr min lq median uq max neval coalesce1(aa, bb, cc) 11.110024 11.137963 11.145723 11.212907 11.270533 10 coalesce1a(aa, bb, cc) 2.906067 2.953266 2.962729 2.971761 3.452251 10 coalesce2(aa, bb, cc) 3.080842 3.115607 3.139484 3.166642 3.198977 10


Tengo una implementación lista para usar llamada coalesce.na en mi paquete misceláneo . Parece ser competitivo, pero no el más rápido. También funcionará para vectores de diferente longitud, y tiene un tratamiento especial para vectores de longitud uno:

expr min lq median uq max neval coalesce(aa, bb, cc) 990.060402 1030.708466 1067.000698 1083.301986 1280.734389 10 coalesce1(aa, bb, cc) 11.356584 11.448455 11.804239 12.507659 14.922052 10 coalesce1a(aa, bb, cc) 2.739395 2.786594 2.852942 3.312728 5.529927 10 coalesce2(aa, bb, cc) 2.929364 3.041345 3.593424 3.868032 7.838552 10 coalesce.na(aa, bb, cc) 4.640552 4.691107 4.858385 4.973895 5.676463 10

Aquí está el código:

coalesce.na <- function(x, ...) { x.len <- length(x) ly <- list(...) for (y in ly) { y.len <- length(y) if (y.len == 1) { x[is.na(x)] <- y } else { if (x.len %% y.len != 0) warning(''object length is not a multiple of first object length'') pos <- which(is.na(x)) x[pos] <- y[(pos - 1) %% y.len + 1] } } x }

Por supuesto, como señaló Kevin, una solución de Rcpp podría ser más rápida por órdenes de magnitud.


Una solución muy simple es usar la función ifelse del paquete base :

coalesce3 <- function(x, y) { ifelse(is.na(x), y, x) }

Aunque parece ser más lento que coalesce2 anterior:

test <- function(a, b, func) { for (i in 1:10000) { func(a, b) } } system.time(test(a, b, coalesce2)) user system elapsed 0.11 0.00 0.10 system.time(test(a, b, coalesce3)) user system elapsed 0.16 0.00 0.15

Puede usar Reduce para que funcione para una cantidad arbitraria de vectores:

coalesce4 <- function(...) { Reduce(coalesce3, list(...)) }


Usando el paquete dplyr :

library(dplyr) coalesce(a, b, c) # [1] 1 2 NA 4 6

Benchamark, no tan rápido como la solución aceptada:

coalesce2 <- function(...) { Reduce(function(x, y) { i <- which(is.na(x)) x[i] <- y[i] x}, list(...)) } microbenchmark::microbenchmark( coalesce(a, b, c), coalesce2(a, b, c) ) # Unit: microseconds # expr min lq mean median uq max neval cld # coalesce(a, b, c) 21.951 24.518 27.28264 25.515 26.9405 126.293 100 b # coalesce2(a, b, c) 7.127 8.553 9.68731 9.123 9.6930 27.368 100 a

Pero en un conjunto de datos más grande, es comparable:

aa <- sample(a, 100000, TRUE) bb <- sample(b, 100000, TRUE) cc <- sample(c, 100000, TRUE) microbenchmark::microbenchmark( coalesce(aa, bb, cc), coalesce2(aa, bb, cc)) # Unit: milliseconds # expr min lq mean median uq max neval cld # coalesce(aa, bb, cc) 1.708511 1.837368 5.468123 3.268492 3.511241 96.99766 100 a # coalesce2(aa, bb, cc) 1.474171 1.516506 3.312153 1.957104 3.253240 91.05223 100 a