按组存储每个单元格中的向量

时间:2015-09-26 03:48:39

标签: r data.table

大家好,我正在寻找一种在每个单元格中存储矢量的方法,这里是样本数据集。

dt1 <- data.table(id = rep(1:2, each = 3), set.a = c(5,1,3,10,4,7))
dt1
   id set.a
1:  1     5
2:  1     1
3:  1     3
4:  2    10
5:  2     4
6:  2     7

现在我想要的是一种将dt1转换为这样的方法:

   id    set.a
1:  1    5,1,3
2:  2 10, 4, 7

这个问题可能非常基本,但确实耗费了我近一个小时。事实上,它是另一份工作的一部分。假设我有另一个数据集:

dt2 <- data.table(id = rep(1:2, each = 3), set.b = c(3,5,9,8,10,4))
dt
   id set.b
1:  1     3
2:  1     5
3:  1     9
4:  2     8
5:  2    10
6:  2     4

我真正想要的是如何计算每个id的重叠,即overlap变量,以指示每个id的set.aset.b的交叉点看起来像

   id overlap
1:  1     5,3
2:  2   10, 4

为了实现这一点,我计划首先将每个id的set变量聚合到一个向量中,然后计算它们的交集,但是我没有在每个单元格中存储一个向量,就像我在开头提到的那样。有人可以帮帮我吗?提前谢谢。

更新

我尝试了akrun和Frank的方式,发现data.table合并方式更有效率。这是一个小基准,谢谢你们两个:)

dt1 <- data.table(id = rep(1:10000, each = 10), set1 = sample(letters[1:24], 100000, replace = T))
dt2 <- data.table(id = rep(1:10000, each = 10), set2 = sample(letters[1:24], 100000, replace = T))

system.time({
re1 <- rbindlist(list(dt1, dt2), idcol=TRUE)[,
  .(overlap=toString(intersect(set1[.id==1], set1[.id==2]))) , by =id]
}) 
# 0.25s

system.time({
re2 <- dt1[dt2, on = c(id = "id", set1 = "set2"), nomatch = 0][, .(ovlp = list(unique(set1))), by = "id"]
})  
# 0.07s

system.time({
dt3 <- dt1[, .(set1 = list(set1)), by = id]
dt4 <- dt2[, .(set2 = list(set2)), by = id]
re3 <- dt3[dt4, nomatch = 0, on = "id"][, .(ov = list(intersect(unlist(set1), unlist(set2)))), by = id]
})  
# 0.21s

2 个答案:

答案 0 :(得分:3)

我们可以使用toStringpaste'set.a'中的元素,按'id'变量分组。

dt1[, .(set.a=toString(set.a)), by = id]
#   id    set.a
#1:  1  5, 1, 3
#2:  2 10, 4, 7

对于第二种情况,我们rbindrbindlist(list(...)数据集添加到单个数据集(idcol=TRUE)和intersect。获取.id的'{1}}的'{1}},使用paste将“id”列和toString元素分组在一起。

 rbindlist(list(dt1, dt2), idcol=TRUE)[,
   .(overlap=toString(intersect(set.a[.id==1], set.a[.id==2]))) , by =id]
#   id overlap
#1:  1    5, 3
#2:  2   10, 4

或者@Richard Scriven在评论中提到,paste我们duplicated后{id}列中'{1}}中rbind的元素。数据集。

rbindlist(list(dt1, dt2))[,
    .(set.a = toString(set.a[duplicated(set.a)])), by = id]

答案 1 :(得分:1)

我会使用合并:

res <- merge(
  dt1[, .(a = list(set.a)), by=id], # <- answer to the first question
  dt2[, .(b = list(set.b)), by=id], 
  by="id"
)[,
  overlap := .(Map(intersect, a, b))
]

#    id        a        b overlap
# 1:  1    5,1,3    3,5,9     5,3
# 2:  2 10, 4, 7  8,10, 4   10, 4

如果速度是一个问题,我怀疑这更快:

dt1[dt2, on = c(id = "id", set.a = "set.b"), nomatch=0][,
  .(overlap = list(set.a))
, by=id]

#    id overlap
# 1:  1     3,5
# 2:  2   10, 4

dt1[dt2,...]只是另一种合并/加入。