【问题标题】:Fast grouping by list column subsets in data.table按 data.table 中的列表列子集快速分组
【发布时间】:2012-12-08 15:56:05
【问题描述】:

我正在处理一个包含深度嵌套列表的列表列的大型(数百万行)数据表,这些列表没有统一的结构、大小或元素顺序(list(x=1,y=2)list(y=2,x=1) 可能同时存在并且应视为相同)。我需要重复执行任意分组,其中包括数据表中的一些列以及列表列中的数据子集。并非所有行都具有与子集匹配的值。

我想出的方法感觉过于复杂。以下是重点:

  • 识别嵌套列表结构中的值。我的方法是使用ul <- unlist(list_col),它“扁平化”嵌套数据结构并构建分层名称以直接访问每个元素,例如, address.country.code.

  • 确保从分组的角度来看,相同未列出数据的排列被视为相等。我的方法是通过 ul[order(names(ul))] 按值的名称对未列出的向量进行排序,然后分配通过引用将结果作为新的字符向量列。

  • 对展平值的子集执行分组。我无法让 by= 以任何方式处理其值为列表或向量的列。因此,我必须找到一种将唯一字符向量映射到简单值的方法。我用digest 做到了这一点。

这是两个主要功能:

# Flatten list column in a data.table
flatten_list_col <- function(dt, col_name, flattened_col_name='props') {

  flatten_props <- function(d) {
    if (length(d) > 0) {
      ul <- unlist(d)
      names <- names(ul)
      if (length(names) > 0) {
        ul[order(names)]          
      } else {
        NA
      }
    } else {
      NA
    }
  }

  flattened <- lapply(dt[[col_name]], flatten_props)
  dt[, as.character(flattened_col_name) := list(flattened), with=F]
}

# Group by properties in a flattened list column
group_props <- function(prop_group, prop_col_name='props') {
  substitute({
    l <- lapply(eval(as.name(prop_col_name)), function(x) x[names(x) %in% prop_group])
    as.character(lapply(l, digest))
  }, list(prop_group=prop_group, prop_col_name=prop_col_name))
}

这是一个可重现的例子:

library(data.table)

dt <- data.table(
  id=c(1,1,1,2,2,2), 
  count=c(1,1,2,2,3,3), 
  d=list(
    list(x=1, y=2), 
    list(y=2, x=1), 
    list(x=1, y=2, z=3),
    list(y=5, abc=list(a=1, b=2, c=3)),
    NA,
    NULL    
    )
)

flatten_list_col(dt, 'd')
dt[, list(total=sum(count)), by=list(id, eval(group_props(c('x', 'y'))))]

输出是:

> flatten_list_col(dt, 'd')
   id count      d   props
1:  1     1 <list>     1,2
2:  1     1 <list>     1,2
3:  1     2 <list>   1,2,3
4:  2     2 <list> 1,2,3,5
5:  2     3     NA      NA
6:  2     3             NA

> dt[, list(total=sum(count)), by=list(id, eval(group_props(c('x', 'y'))))]
   id                      group_props total
1:  1 325c6bbb2c33456d0301cf3909dd1572     4
2:  2 7aa1e567cd0d6920848d331d3e49fb7e     2
3:  2 ee7aa3b9ffe6bffdee83b6ecda90faac     6

这种方法很有效,但效率很低,因为需要对列表进行展平和排序以及计算摘要。我想知道以下几点:

  1. 是否可以通过直接从列表列中检索值而不必创建扁平列来完成此操作?这可能需要将选定的属性指定为表达式,而不是简单的名称。

  2. 有没有办法解决对digest 的需求?

【问题讨论】:

  • 我无法让您编辑的函数在 data.table 1.8.7 上运行,
  • 那是编辑过程中的意外提交。固定。
  • 'NA' 是一个字符变量 'NA' 不是真正意义上的 NA 值。这真的是你的意思吗?
  • @mnel,是的,除非 NA 可以在不改变模式的情况下混合到字符向量中。
  • @mnel,我有一个可行的解决方案,我希望你能想到。

标签: r grouping data.table aggregation


【解决方案1】:

这里有很多问题。最重要的(也是由于其他原因您还没有找到)是您通过引用进行分配,但尝试替换的值比您通过引用的空间要多。

举这个非常简单的例子

DT <- data.table(x=1, y = list(1:5))
DT[,new := unlist(y)]
Warning message:
In `[.data.table`(DT, , `:=`(new, unlist(y))) :
  Supplied 5 items to be assigned to 1 items of column 'new' (4 unused)

您将丢失新创建列表中除第一个nrow(DT) 之外的所有项目。它们不会对应于 data.table 的行

因此,您必须创建一个足够大的新data.table,以便分解这些变量列表。这不可能通过引用来实现。

 newby <- dt[,list(x, props = as.character(unlist(data))), by = list(newby = seq_len(nrow(dt)))][,newby:=NULL]
newby


   x props
 1: 1     1
 2: 1     2
 3: 1     2
 4: 1     1
 5: 1    10
 6: 2     1
 7: 2     2
 8: 2     3
 9: 2     5
10: 2     1
11: 2     2
12: 2     3
13: 3    NA
14: 3    NA

请注意,需要 as.character 以确保所有值都是相同的类型,并且是在转换中不会丢失数据的类型。目前,您在数字/整数数据列表中有一个逻辑 NA 值。


另一个编辑强制所有组件为字符(甚至是 NA)。 props 现在是一个列表,每行有 1 个字符向量。

flatten_props 1){ ul

dt[, props := lapply(data, flatten_props)]
dt
   x   data   props
1: 1 <list>     1,2
2: 1 <list>  10,1,2
3: 2 <list>   1,2,3
4: 2 <list> 1,2,3,5
5: 3     NA      NA
6: 3   

dt[,lapply(props,class)]
          V1        V2        V3        V4        V5        V6
1: character character character character character character

【讨论】:

  • 感谢编辑建议:我会更新。至于值爆炸的一般问题,为什么不将向量包装在list() 中?还有一件事:as.character() 删除名称;我不确定我们是否需要它。
  • 再想一想,如果数百万只有一个元素,我查看了数据而不是单个列表,因此我将保持名称长度检查 &gt;0 以避免出现零 vs 的特殊情况. else 子句中的一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-26
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 2013-04-17
相关资源
最近更新 更多