首页 文章

R使用列表列重新融合data.table

提问于
浏览
5

我有一个大的(数百万行)融化了 data.table ,通常在 variablevalue 列中展开 melt 样式 . 我需要以宽泛的形式转换表(滚动变量) . 问题是数据表还有一个名为 data 的列表列,我需要保留它 . 这使得无法使用 reshape2 因为 dcast 无法处理非原子列 . 因此,我需要自己卷起来 .

由于列表列,previous question关于使用熔化数据表的答案不适用于此处 .

我对我提出的解决方案不满意 . 我正在寻找更简单/更快实现的建议 .

x <- LETTERS[1:3]
dt <- data.table(
  x=rep(x, each=2),
  y='d',
  data=list(list(), list(), list(), list(), list(), list()),
  variable=rep(c('var.1', 'var.2'), 3),
  value=seq(1,6)
  )

# Column template set up
list_template <- Reduce(
  function(l, col) { l[[col]] <- col; l }, 
  unique(dt$variable),
  list())

# Expression set up
q <- substitute({
  l <- lapply(
    list_template, 
    function(col) .SD[variable==as.character(col)]$value)
  l$data = .SD[1,]$data
  l
}, list(list_template=list_template))

# Roll up
dt[, eval(q), by=list(x, y)]

   x y var.1 var.2   data
1: A d     1     2 <list>
2: B d     3     4 <list>
3: C d     5     6 <list>

2 回答

  • 1

    我有一些欺骗方法可能会做到这一点 - 重要的是,我认为每个x,y,列表组合都是独一无二的!如果没有,请忽略 .

    我将创建两个单独的数据表,第一个是没有数据列表对象的数据,第二个只有唯一的数据列表对象和一个键 . 然后将它们合并在一起以获得所需的结果 .

    require(data.table)
    require(stringr)
    require(reshape2)
    
    x <- LETTERS[1:3]
    dt <- data.table(
      x=rep(x, each=2),
      y='d',
      data=list(list("a","b"), list("c","d")),
      variable=rep(c('var.1', 'var.2'), 3),
      value=seq(1,6)
      )
    
    
    # First create the dcasted datatable without the pesky list objects:
    dt_nolist <- dt[,list(x,y,variable,value)]
    dt_dcast <- data.table(dcast(dt_nolist,x+y~variable,value.var="value")
                           ,key=c("x","y"))
    
    
    # Second: create a datatable with only unique "groups" of x,y, list
    dt_list <- dt[,list(x,y,data)]
    
    # Rows are duplicated so I'd like to use unique() to get rid of them, but
    # unique() doesn't work when there's list objects in the data.table.
    # Instead so I cheat by applying a value to each row within an x,y "group" 
    # that is unique within EACH group, but present within EVERY group.
    # Then just simply subselect based on that unique value.
    # I've chosen rank(), but no doubt there's other options
    
    dt_list <- dt_list[,rank:=rank(str_c(x,y),ties.method="first"),by=str_c(x,y)]
    
    # now keep only one row per x,y "group"
    dt_list <- dt_list[rank==1]
    setkeyv(dt_list,c("x","y"))
    
    # drop the rank since we no longer need it
    dt_list[,rank:=NULL]
    
    # Finally just merge back together
    dt_final <- merge(dt_dcast,dt_list)
    
  • 1

    自2013年以来,这个古老的问题激起了我的好奇心,因为它已经有了很大的改善 .

    但是,即使使用 data.table 版本1.11.4

    dcast(dt, x + y + data ~ variable)
    

    仍然会返回错误

    公式中指定的列不能是类型列表

    解决方法遵循jonsedar's answer的大纲:

    • 将非列表列从长格式重新格式化为宽格式

    • 汇总列表列 dataxy 分组

    • xy 上加入两个部分结果

    但使用实际 data.table 语法的功能,例如 on 参数:

    dcast(dt, x + y ~ variable)[
      dt[, .(data = .(first(data))), by = .(x, y)], on = .(x, y)]
    

    x y var.1 var.2数据
    1:A d 1 2 <list>
    2:B d 3 4 <list>
    3:C d 5 6 <list>

    列表列 data 通过获取第一个元素进行聚合 . 这符合OP的代码行

    l$data = .SD[1,]$data
    

    这也选择了第一个元素 .

相关问题