2011-05-09 69 views
29

剛剛和同事談過這件事,我們認爲值得看看SO土地上的人們不得不說什麼。假設我有一個包含N個元素的列表,其中每個元素都是一個長度爲X的向量。現在假設我想將其轉換爲一個data.frame。與R中的大多數事情一樣,使用plyr軟件包,組合do.callcbind,預先分配DF並填充它等等,可以使用多種方法來剝離諺語貓,如as.dataframe最有效的列表data.frame方法?

提出的問題是當N或X(在我們的例子中是X)變得非常大時發生的情況。當效率(尤其是記憶方面)的本質是否存在時,是否存在一種明顯優於貓皮膚的方法?

回答

26

由於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 

更新 - 這是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 

更新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 
+2

休假 「可能」 出答案這是正確的。如果你使用這些調用來創建一個函數,並用長度命令替換知道n的作弊,那也是正確的。刪除所有廣泛的檢查後,您的新函數大致等同於data.frame()。所以,如果你確實知道你把這個電話轉換成正確的輸入,那麼只需按照Josh的建議來加快速度。如果你不確定,那麼data.frame是更安全的,並且do.call(data.frame,x))是下一個最快(奇怪的是)。 – John 2011-05-09 22:18:25

+2

請看'plyr :: quickdf'確切的這個功能。 – hadley 2011-05-09 23:25:56

+0

@hadley:'plyr :: quickdf'不提供這個功能;即它不會生成唯一的列名稱。 'plyr ::: make_names'只替換缺失的名字,沒有像'base :: make.names'這樣的'unique ='arg。 – 2011-05-10 00:58:30

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

}) 
+1

setattr(h,「class」,「data.frame」)應該是即時的,根本不需要拷貝。 – 2012-09-12 08:07:42

+0

@MatthewDowle - 正如'setattr(h,「class」,「data.table」)';)(非常酷,BTW)。 – 2012-09-12 08:16:41

+0

@ JoshO'Brien的確:)只有在過去幾天才意識到''setattr''說'x'必須是'data.table'(感謝對datatable-help的評論)。 'setattr'實際上是用來處理任何事情的。將修復文件。它也會返回它的輸入,所以如果需要的話,你可以在後面加入'[i,j,by]'(也就是說,如果你把它包裝到一個別名中:'setDT(DF)[i,j,by]')。 – 2012-09-12 08:34:24

相關問題