【问题标题】:How to ungroup list columns in data.table?如何取消分组 data.table 中的列表列?
【发布时间】:2016-04-14 00:48:54
【问题描述】:

tidyr 提供了unnest 函数,帮助扩展列表列。

这类似于 kdb 中快得多 (20 倍) 的 ungroup 函数。

我正在寻找一个类似(但速度更快)的函数,假设 data.table 包含多个列表列,每个列在每行上具有相同数量的元素,将扩展 data.table。

这是this post的扩展。

library(data.table)
library(tidyr)
t = Sys.time()
DT = data.table(a=c(1,2,3),
                b=c('q','w','e'),
                c=list(rep(t,2),rep(t+1,3),rep(t,0)),
                d=list(rep(1,2),rep(20,3),rep(1,0)))

print(DT)
   a b                                                           c        d
1: 1 q                     2016-01-09 09:55:14,2016-01-09 09:55:14      1,1
2: 2 w 2016-01-09 09:55:15,2016-01-09 09:55:15,2016-01-09 09:55:15 20,20,20
3: 3 e                                                                     

print(unnest(DT))
Source: local data frame [5 x 4]

      a     b                   c     d
  (dbl) (chr)              (time) (dbl)
1     1     q 2016-01-09 09:55:14     1
2     1     q 2016-01-09 09:55:14     1
3     2     w 2016-01-09 09:55:15    20
4     2     w 2016-01-09 09:55:15    20
5     2     w 2016-01-09 09:55:15    20

这是我自己的尝试……这似乎快了 2 倍,但应该大大改进……

dtUngroup <- function(DT){
  colClasses <- lapply(DT,FUN=class)
  listCols <- colnames(DT)[colClasses=='list']
  if(length(listCols)>0){
    nonListCols <- setdiff(colnames(DT),listCols)
    nbListElem <- unlist(DT[,lapply(.SD,FUN=lengths),.SDcols=(listCols[1L])])
    DT1 <- DT[,lapply(.SD,FUN=rep,times=(nbListElem)),.SDcols=(nonListCols)]
    DT1[,(listCols):=DT[,lapply(.SD,FUN=function(x) do.call('c',x)),.SDcols=(listCols)]]
    return(DT1)
  }
  return(DT)
} 
dtUngroup(DT)[]
   a b                   c  d
1: 1 q 2016-01-09 09:55:14  1
2: 1 q 2016-01-09 09:55:14  1
3: 2 w 2016-01-09 09:55:15 20
4: 2 w 2016-01-09 09:55:15 20
5: 2 w 2016-01-09 09:55:15 20

【问题讨论】:

  • 好吧,不要把 POSIXct 列出来……随意起草一个答案……
  • 您可以使用以下命令使其更短:DT[, lapply(.SD, unlist), by = 1:nrow(DT)]
  • oce 包中使用numberAsPOSIXct() 和Jaap 的想法,下面可能是你所追求的:DT[, lapply(.SD, unlist), by = 1:nrow(DT)][, c := numberAsPOSIXct(c)][]
  • 为什么是这个而不是 FUN=function(x) do.call('c',x) ?我同意我可以节省 2 行...但它更快吗?
  • @jazzurro 只使用 asPOSIXct 也可以

标签: r data.table kdb tidyr


【解决方案1】:

使用:

na.omit(DT[, lapply(.SD, unlist), a][, c := as.POSIXct(c, origin="1970-01-01")])

给予:

   a b                   c  d
1: 1 q 2016-01-09 12:17:24  1
2: 1 q 2016-01-09 12:17:24  1
3: 2 w 2016-01-09 12:17:25 20
4: 2 w 2016-01-09 12:17:25 20
5: 2 w 2016-01-09 12:17:25 20

a列中的值对于每一行来说不是唯一的,你可以使用:

na.omit(DT[, lapply(.SD, unlist), by=1:nrow(DT)][, c := as.POSIXct(c, origin="1970-01-01")])

一个基准:

> microbenchmark(dtUngroup(DT)[], jaap())
Unit: milliseconds
            expr      min       lq     mean   median       uq      max neval cld
 dtUngroup(DT)[] 3.935677 4.005596 4.189208 4.066196 4.227372 6.750338   100   b
          jaap() 1.977175 2.039830 2.094536 2.074314 2.132525 2.309848   100  a 

【讨论】:

  • 我需要将它包装在一个函数中来处理几个 POSIXct 列,我很确定这与许多其他类型的情况相同(data.table:::ITime for示例)
  • @statquant 参见here 示例
  • 没有真正来自列表列的类必须动态重新转换,这不仅仅是在做 DT[,c:=as.whatever(c)]。所以我支持你,但我不认为这个问题的答案是 1. 我们没有得到所需的输出(即使这只是一个 is.na ) 2. 没有基准 3. 这个解决方案失去了列的类型(或不提供多列解决方案)
猜你喜欢
  • 2021-07-11
  • 1970-01-01
  • 2021-01-11
  • 2018-05-10
  • 1970-01-01
  • 2012-12-08
  • 2012-11-13
相关资源
最近更新 更多