data.frame方法最有效的列表?

geo*_*try 33 performance memory-management r dataframe data.table

刚刚和同事谈过这个问题,我们认为看看SO土地上的人们不得不说出来是值得的.假设我有一个包含N个元素的列表,其中每个元素都是长度为X的向量.现在假设我想将其转换为data.frame.与R中大多数事情一样,有剥皮众所周知的猫的多种方式,比如as.dataframe,使用plyr包,comboing do.callcbind,预分配的DF和填充它,和其他人.

提出的问题是当N或X(在我们的例子中是X)变得非常大时会发生什么.当效率(特别是在记忆方面)至关重要时,是否有一种猫皮肤方法显着优越?

Jos*_*ich 27

由于a data.frame已经是一个列表,并且您知道每个列表元素的长度(X)相同,因此最快的事情可能就是更新classrow.names属性:

set.seed(21)
n <- 1e6
x <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
x <- c(x,x,x,x,x,x)

system.time(a <- as.data.frame(x))
system.time(b <- do.call(data.frame,x))
system.time({
  d <- x  # Skip 'c' so Joris doesn't down-vote me! ;-)
  class(d) <- "data.frame"
  rownames(d) <- 1:n
  names(d) <- make.unique(names(d))
})

identical(a, b)  # TRUE
identical(b, d)  # TRUE
Run Code Online (Sandbox Code Playgroud)

更新 - 这比创建快2倍d:

system.time({
  e <- x
  attr(e, "row.names") <- c(NA_integer_,n)
  attr(e, "class") <- "data.frame"
  attr(e, "names") <- make.names(names(e), unique=TRUE)
})

identical(d, e)  # TRUE
Run Code Online (Sandbox Code Playgroud)

更新2 - 我忘记了内存消耗.最后一次更新制作了两份副本e.使用该attributes功能可将其减少到仅一个副本.

set.seed(21)
f <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
f <- c(f,f,f,f,f,f)
tracemem(f)
system.time({  # makes 2 copies
  attr(f, "row.names") <- c(NA_integer_,n)
  attr(f, "class") <- "data.frame"
  attr(f, "names") <- make.names(names(f), unique=TRUE)
})

set.seed(21)
g <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
g <- c(g,g,g,g,g,g)
tracemem(g)
system.time({  # only makes 1 copy
  attributes(g) <- list(row.names=c(NA_integer_,n),
    class="data.frame", names=make.names(names(g), unique=TRUE))
})

identical(f,g)  # TRUE
Run Code Online (Sandbox Code Playgroud)

  • 请参阅`plyr :: quickdf`以了解此功能. (3认同)
  • @hadley:根据谁来规范?我在手册中找不到任何讨论,`attr <-`和`structure`似乎在核心R源中同样经常使用...而`structure`使用`attributes <-`. (3认同)
  • 将"可能"留在答案之外,这是正确的.如果你使用这些调用创建一个函数并用长度命令替换知道n的作弊也是正确的.在删除所有大量检查后,您的新函数大致相当于data.frame().所以,如果你肯定知道你正在给正确的输入拨打电话,那就去做乔希推荐的速度.如果你不确定那么data.frame更安全,do.call(data.frame,x))是下一个最快的(奇怪的是). (2认同)
  • "tracemem"的精彩演示,以及列表和数据框之间差异的良好说明. (2认同)

mne*_*nel 10

data.table鉴于需要大数据集的效率,这似乎需要一个建议.值得注意的是setattr通过引用设置并且不复制

library(data.table)
set.seed(21)
n <- 1e6
h <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
h <- c(h,h,h,h,h,h)
tracemem(h)

system.time({h <- as.data.table(h)
            setattr(h, 'names', make.names(names(h), unique=T))})
Run Code Online (Sandbox Code Playgroud)

as.data.table然而,确实复制.


编辑 - 没有复制版本

使用@ MatthewDowle的建议setattr(h,'class','data.frame'),将通过引用转换为data.frame(无副本)

set.seed(21)
n <- 1e6
i <- list(x=rnorm(n), y=rnorm(n), z=rnorm(n))
i <- c(i,i,i,i,i,i)
tracemem(i)

system.time({  
  setattr(i, 'class', 'data.frame')
  setattr(i, "row.names", c(NA_integer_,n))

  setattr(i, "names", make.names(names(i), unique=TRUE))

})
Run Code Online (Sandbox Code Playgroud)