【问题标题】:Most efficient list to data.frame method?data.frame 方法的最有效列表?
【发布时间】:2011-08-22 00:21:24
【问题描述】:

刚刚与同事就此进行了交谈,我们认为值得看看 SO 土地上的人们怎么说。假设我有一个包含 N 个元素的列表,其中每个元素都是长度为 X 的向量。现在假设我想将其转换为 data.frame。与 R 中的大多数东西一样,有多种方法可以给众所周知的猫剥皮,例如 as.dataframe、使用 plyr 包、将 do.callcbind 组合、预分配 DF 并填充它等等。

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

【问题讨论】:

    标签: performance r memory-management dataframe data.table


    【解决方案1】:

    鉴于需要大型数据集的效率,这似乎需要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))
    
    })
    

    【讨论】:

    • setattr(h,"class","data.frame") 应该是即时的,根本没有复制。
    • @MatthewDowle -- 正如setattr(h, "class", "data.table") ;) (非常酷,顺便说一句)。
    • @JoshO'Brien 确实 :) 最近几天才意识到?setattrx 必须是data.table(感谢对datatable-help 的评论)。 setattr 实际上是为了处理任何事情。将修复文档。它也返回它的输入,因此您可以在需要时复合[i,j,by](例如,如果您将其包装成别名:setDT(DF)[i,j,by])。
    • @MatthewDowle -- 是的,我尝试了您的代码,很高兴看到它完成了到data.frame 的转换,而无需制作任何副本。很好的黑客攻击!
    • @JoshO'Brien setattr 实际上只是 R 的 C 级别 setAttrib API 函数的单行封装。包bit 具有相同的功能,顺便说一句。它也有vecseq(我刚刚看到),看起来非常方便。可能值得回顾 bit 看看它还有什么其他的宝石(自我说明)。
    【解决方案2】:

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

    更新 - 这比创建 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
    

    【讨论】:

    • 将“可能”排除在答案之外,这是正确的。如果您使用这些调用创建函数并用长度命令替换知道 n 的作弊,这也是正确的。删除所有广泛检查后,您的新功能大致相当于 data.frame() 。因此,如果您确定您正在处理正确的呼叫,那么只需按照 Josh 建议的速度进行操作。如果您不确定,那么 data.frame 更安全,并且 do.call(data.frame, x)) 是下一个最快的(奇怪的是)。
    • 查看plyr::quickdf 了解这个功能。
    • @John:“可能”是指“据我所知”。如果我不确定,我尽量不要说得太过分。
    • tracemem 的精彩演示,很好地说明了列表和数据框之间的区别。
    • @hadley:根据谁的规范?我在手册中找不到对此的任何讨论,attr&lt;-structure 似乎在核心 R 源代码中同样经常使用......而structure 使用 attributes&lt;-
    猜你喜欢
    • 2012-02-04
    • 2017-02-03
    • 1970-01-01
    • 2017-01-28
    • 2014-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-19
    相关资源
    最近更新 更多