do.call(rbind,list)表示列数不均匀

h.l*_*l.m 17 data-binding r list

我有一个列表,每个元素都是一个字符向量,长度不同我想将数据绑定为行,以便列名称"排列",如果有额外数据则创建列,如果有数据丢失然后创建NA

下面是我正在使用的数据的模拟示例

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)]
Run Code Online (Sandbox Code Playgroud)

如果我确定每个元素的格式是相同的,那么下面的行通常就是我要做的...

do.call(rbind,x)
Run Code Online (Sandbox Code Playgroud)

我希望有人提出了一个很好的小解决方案,匹配列名称并用空格填充空白,NA同时添加新列,如果在绑定过程中找到新列...

Aru*_*run 28

rbind.fill是一个很棒的函数,在data.frames列表上表现得非常好.但恕我直言,对于这种情况,当列表仅包含(命名)向量时,它可以更快地完成.

rbind.fill方法

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

一种更直接的方式(至少对这种情况有效):

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)
}
Run Code Online (Sandbox Code Playgroud)

基本上,我们获得总的唯一名称来形成最终data.frame的列.然后,我们创建一个length = input的列表,并用其他值填充其余值NA.这可能是"最棘手"的部分,因为我们要在填充NA时匹配名称.然后,我们最后定的名字一次列(这可以通过使用基准来设定setnamesdata.table包以及如果需要的话).


现在进行一些基准测试:

数据:

# 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())
Run Code Online (Sandbox Code Playgroud)

功能:

# 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)
}
Run Code Online (Sandbox Code Playgroud)

更新(添加了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)
}
Run Code Online (Sandbox Code Playgroud)

标杆:

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
Run Code Online (Sandbox Code Playgroud)


GSe*_*See 9

如果你想让结果成为一个矩阵......

我最近为一个想要将矢量转换成矩阵的同事写了这个函数.

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"
Run Code Online (Sandbox Code Playgroud)