【问题标题】:R: loop over columns in data.tableR:循环遍历 data.table 中的列
【发布时间】:2012-05-22 00:51:12
【问题描述】:

我想确定一个大data.table的列类。

colClasses <- sapply(DT, FUN=function(x)class(x)[1])

有效,但显然本地副本存储到内存中:

> memory.size()
[1] 687.59
> colClasses <- sapply(DT, class)
> memory.size()
[1] 1346.21

循环似乎是不可能的,因为 data.table "with=FALSE" 总是导致 data.table。

一个快速且非常肮脏的方法是:

DT1 <- DT[1, ]
colClasses <- sapply(DT1, FUN=function(x)class(x)[1])

什么是最优雅和最有效的方法?

【问题讨论】:

  • 不确定我是否关注。为什么不只是sapply(DT,class)
  • 以上文字中添加的时间
  • @MatthewDowle:我认为 OP 意味着 sapply 使用 data.table 的子集创建临时变量,以便为每一列传递给 FUN。由于它的 data.table 非常大并且有很多列,因此效率不高。为此,他的解决方法是先将data.table缩减为一行,然后调用sapply...
  • @digEmAll 啊哈,我现在明白了,谢谢。

标签: r data.table sapply


【解决方案1】:

我认为这样的方法没有任何问题

colClasses <- sapply(head(DT1,1), FUN=class)

它基本上是你的快速'n'dirty解决方案,但可能更清晰(即使不是那么多)......

【讨论】:

  • 这确实是一个很好的解决方案,但没有我希望的那么优雅。
【解决方案2】:

简单调查了一下,貌似是data.table的bug。

> DT = data.table(a=1:1e6,b=1:1e6,c=1:1e6,d=1:1e6)
> Rprofmem()
> sapply(DT,class)
        a         b         c         d 
"integer" "integer" "integer" "integer" 
> Rprofmem(NULL)
> noquote(readLines("Rprofmem.out"))
[1] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply"       
[2] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply" 
[3] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply"   
[4] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply" 

> tracemem(DT)
> sapply(DT,class)
tracemem[000000000431A290 -> 00000000065D70D8]: as.list.data.table as.list lapply sapply 
        a         b         c         d 
"integer" "integer" "integer" "integer" 

所以,看看as.list.data.table

> data.table:::as.list.data.table
function (x, ...) 
{
    ans <- unclass(x)
    setattr(ans, "row.names", NULL)
    setattr(ans, "sorted", NULL)
    setattr(ans, ".internal.selfref", NULL)
    ans
}
<environment: namespace:data.table>
> 

请注意第一行的讨厌的unclass?unclass 确认它需要对其参数的深层副本。从这个快速浏览来看,sapplylapply 似乎没有进行复制(我认为他们没有这样做,因为 R 擅长写时复制,而那些不是写),而是as.list 中的 lapply(发送到 as.list.data.table)。

所以,如果我们避开unclass,它应该会加快速度。让我们试试吧:

> DT = data.table(a=1:1e7,b=1:1e7,c=1:1e7,d=1:1e7)
> system.time(sapply(DT,class))
   user  system elapsed 
   0.28    0.06    0.35 
> system.time(sapply(DT,class))  # repeat timing a few times and take minimum
   user  system elapsed 
   0.17    0.00    0.17 
> system.time(sapply(DT,class))
   user  system elapsed 
   0.13    0.04    0.18 
> system.time(sapply(DT,class))
   user  system elapsed 
   0.14    0.03    0.17 
> assignInNamespace("as.list.data.table",function(x)x,"data.table")
> data.table:::as.list.data.table
function(x)x
> system.time(sapply(DT,class))
   user  system elapsed 
      0       0       0 
> system.time(sapply(DT,class))
   user  system elapsed 
   0.01    0.00    0.02 
> system.time(sapply(DT,class))
   user  system elapsed 
      0       0       0 
> sapply(DT,class)
        a         b         c         d 
"integer" "integer" "integer" "integer" 
> 

所以,是的,无限更好。

我已经提出bug report #2000 来删除as.list.data.table 方法,因为data.table is() 也已经是list。这实际上可能会加速很多成语,例如lapply(.SD,...)。 [编辑:这已在 v1.8.1 中修复]。

感谢您提出这个问题!

【讨论】:

  • 信息量很大的帖子。感谢您展示用于调试此问题的步骤。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-28
相关资源
最近更新 更多