r list data-binding

do.call(rbind, list) para un número desigual de columnas



data-binding (2)

Tengo una lista, con cada elemento es un vector de caracteres, de diferentes longitudes Me gustaría vincular los datos como filas, de modo que los nombres de columna ''alinear'' y si hay datos adicionales, a continuación, crear columna y si hay datos faltantes luego crea NA

A continuación se muestra un ejemplo falso de los datos con los que estoy trabajando

x <- list() x[[1]] <- letters[seq(2,20,by=2)] names(x[[1]]) <- LETTERS[c(1:length(x[[1]]))] x[[2]] <- letters[seq(3,20, by=3)] names(x[[2]]) <- LETTERS[seq(3,20, by=3)] x[[3]] <- letters[seq(4,20, by=4)] names(x[[3]]) <- LETTERS[seq(4,20, by=4)]

La línea siguiente sería normalmente lo que haría si estuviera seguro de que el formato de cada elemento era el mismo ...

do.call(rbind,x)

Tenía la esperanza de que alguien hubiera encontrado una pequeña solución que coincida con los nombres de las columnas y llene los espacios en blanco con las de NA , mientras que agrega nuevas columnas si en el proceso de enlace se encuentran nuevas columnas ...


Si quieres que el resultado sea una matriz ...

Hace poco escribí esta función para un compañero de trabajo que quería vincular vectores en una matriz.

foo <- function (...) { dargs <- list(...) if (!all(vapply(dargs, is.vector, TRUE))) stop("all inputs must be vectors") if (!all(vapply(dargs, function(x) !is.null(names(x)), TRUE))) stop("all input vectors must be named.") all.names <- unique(names(unlist(dargs))) out <- do.call(rbind, lapply(dargs, `[`, all.names)) colnames(out) <- all.names out } R > do.call(foo, x) A B C D E F G H I J L O R P T [1,] "b" "d" "f" "h" "j" "l" "n" "p" "r" "t" NA NA NA NA NA [2,] NA NA "c" NA NA "f" NA NA "i" NA "l" "o" "r" NA NA [3,] NA NA NA "d" NA NA NA "h" NA NA "l" NA NA "p" "t"


rbind.fill es una función increíble que funciona muy bien en la lista de data.frames. Pero en mi humilde opinión, en este caso, podría hacerse mucho más rápido cuando la lista contenga solo vectores (con nombre).

La forma rbind.fill

require(plyr) rbind.fill(lapply(x,function(y){as.data.frame(t(y),stringsAsFactors=FALSE)}))

Una manera más directa (y eficiente para este escenario al menos):

rbind.named.fill <- function(x) { nam <- sapply(x, names) unam <- unique(unlist(nam)) len <- sapply(x, length) out <- vector("list", length(len)) for (i in seq_along(len)) { out[[i]] <- unname(x[[i]])[match(unam, nam[[i]])] } setNames(as.data.frame(do.call(rbind, out), stringsAsFactors=FALSE), unam) }

Básicamente, obtenemos nombres únicos totales para formar las columnas del data.frame final. Luego, creamos una lista con length = input y simplemente llenamos el resto de los valores con NA . Esta es probablemente la parte más "difícil" ya que tenemos que coincidir con los nombres mientras llenamos NA. Y luego, fijamos los nombres una vez finalmente en las columnas (que se pueden establecer por referencia usando setnames del paquete data.table también, si es necesario).

Ahora a algunos puntos de referencia:

Datos:

# generate some huge random data: set.seed(45) sample.fun <- function() { nam <- sample(LETTERS, sample(5:15)) val <- sample(letters, length(nam)) setNames(val, nam) } ll <- replicate(1e4, sample.fun())

Funciones:

# plyr''s rbind.fill version: rbind.fill.plyr <- function(x) { rbind.fill(lapply(x,function(y){as.data.frame(t(y),stringsAsFactors=FALSE)})) } rbind.named.fill <- function(x) { nam <- sapply(x, names) unam <- unique(unlist(nam)) len <- sapply(x, length) out <- vector("list", length(len)) for (i in seq_along(len)) { out[[i]] <- unname(x[[i]])[match(unam, nam[[i]])] } setNames(as.data.frame(do.call(rbind, out), stringsAsFactors=FALSE), unam) }

Actualización (también se agregó la función de GSee):

foo <- function (...) { dargs <- list(...) all.names <- unique(names(unlist(dargs))) out <- do.call(rbind, lapply(dargs, `[`, all.names)) colnames(out) <- all.names as.data.frame(out, stringsAsFactors=FALSE) }

Benchmarking:

require(microbenchmark) microbenchmark(t1 <- rbind.named.fill(ll), t2 <- rbind.fill.plyr(ll), t3 <- do.call(foo, ll), times=10) identical(t1, t2) # TRUE identical(t1, t3) # TRUE Unit: milliseconds expr min lq median uq max neval t1 <- rbind.named.fill(ll) 243.0754 258.4653 307.2575 359.4332 385.6287 10 t2 <- rbind.fill.plyr(ll) 16808.3334 17139.3068 17648.1882 17890.9384 18220.2534 10 t3 <- do.call(foo, ll) 188.5139 204.2514 229.0074 339.6309 359.4995 10