为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

慕桂英546537 2019-11-19 10:44:11

我有一个列表,每个元素都是一个字符向量,长度不同,我想将数据绑定为行,以便列名“排成一行”,如果有多余的数据,则创建列,如果缺少数据然后创建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)]

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


do.call(rbind,x)

我希望有人提出了一个不错的小解决方案,该解决方案可以匹配列名称并用NAs 填充空格,如果在绑定过程中找到了新列,则添加新列...


查看完整描述

3 回答

?
慕工程0101907

rbind.fill是一个很棒的功能,在data.frames列表上确实表现出色。但是恕我直言,在这种情况下,当列表仅包含(命名)向量时,可以更快地完成。


该rbind.fill方法

require(plyr)

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)

}

基本上,我们获得了唯一的总名称,以形成最终data.frame的列。然后,我们创建一个长度为input的列表,并用填充其余的值NA。这可能是“最棘手的”部分,因为我们在填写NA时必须匹配名称。然后,我们最后一次在列中设置名称(如果需要,也可以使用setnamesfrom data.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())

职能:

# 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)

}

更新(还添加了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)

}

基准测试:

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


查看完整回答
反对 回复 2019-11-19
?
繁花如伊

如果您希望结果为矩阵...


我最近为一个想将向量重新绑定到矩阵中的同事编写了此函数。


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"


查看完整回答
反对 回复 2019-11-19
?
一只甜甜圈

这是使用该软件包的版本data.table,对于非常大的数据,速度要快一些。它使用函数rbindlist及其参数fill=TRUE传递给函数do.call。


rbindlist(l2, fill=TRUE)

由于需要使用转换字符向量,因此增加了少量开销as.list。根据数据的生成方式,此段落还可以增加处理时间。

另一方面,它似乎在大型数据集上的执行速度更快。

返回data.table。


我重写了@Arun和@GSee的示例以生成更大的示例。


数据

# 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)  

}

l1 <- replicate(1e6, sample.fun()) # Arun's data, just bigger

l2 <- lapply(l1, as.list) # same data converted with as.list

职能

library(microbenchmark)

library(data.table)

# Arun's function

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)

}


# GSee's function

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)

}

标杆管理

microbenchmark(t1 <- rbind.named.fill(l1), 

               t2 <- rbindlist(l2, fill=TRUE),

               t3 <- do.call(foo, l1),

               times=10)

#> Unit: seconds

#>                                 expr      min        lq        mean    median        uq      max neval

#> t1 <- rbind.named.fill(l1)      6.536782  7.545538   9.118771  9.304844 10.505814 11.28260    10

#> t2 <- rbindlist(l2, fill=TRUE)  5.250387  5.787712   6.910340  6.226065  7.579503 10.40524    10

#> t3 <- do.call(foo, l1)          9.590615 11.043557  13.504694 12.550535 15.364464 19.95877    10



identical(t1, data.frame(t2))

#> [1] TRUE

identical(t3, data.frame(t2))

#> [1] TRUE


查看完整回答
反对 回复 2019-11-19
  • 3 回答
  • 0 关注
  • 40 浏览
我要回答

相关问题推荐

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信