刚刚和同事谈过这个问题,我们认为看看SO土地上的人们不得不说出来是值得的。假设我有一个包含N个元素的列表,其中每个元素都是长度为X的向量。现在假设我想将其转换为data.frame。与R中的大多数内容一样,使用plyr包,将as.dataframe
与do.call
组合,预先分配DF和填充,有多种方法可以为谚语猫设置外观,例如cbind
。它在和其他人。
提出的问题是当N或X(在我们的例子中是X)变得非常大时会发生什么。当效率(特别是在记忆力方面)至关重要时,是否有一种猫皮肤方法显着优越?
答案 0 :(得分:27)
由于data.frame
已经是一个列表,并且您知道每个列表元素的长度(X)相同,最快的事情可能就是更新class
和row.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
更新 - 这比创建d
快〜2倍:
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
更新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
答案 1 :(得分: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))})
然而, 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))
})