R(data.table):多列中快速的值匹配计数

时间:2017-04-26 22:48:46

标签: r data.table

是否有一种快速计算多个向量中出现的值出现在多个其他向量中的次数?这是一个例子:

library(data.table)
names<-c(rep('apple',4),rep('banana',3),rep('cantalope',2),'date')
set.seed(38291)
v1<-data.table(municipality=rep('A',6),village=rep('1',6),
               last=sample(names,6,replace=TRUE),
               middle=sample(names,6,replace=TRUE),id=c(1:6))
v2<-data.table(municipality=rep('A',4),village=rep('2',4),
               last=sample(names,4,replace=TRUE),
               middle=sample(names,4,replace=TRUE),id=c(7:10))
v1
#    municipality village      last    middle id
# 1:            A       1    banana cantalope  1
# 2:            A       1 cantalope    banana  2
# 3:            A       1 cantalope cantalope  3
# 4:            A       1     apple     apple  4
# 5:            A       1    banana     apple  5
# 6:            A       1     apple     apple  6
v2
#    municipality village      last    middle id
# 1:            A       2      date cantalope  7
# 2:            A       2     apple      date  8
# 3:            A       2 cantalope    banana  9
# 4:            A       2     apple cantalope 10
DT = rbind(v1, v2)

我想计算村庄1和村庄2中个人之间的家庭关系数量。跨村庄的家庭关系由个人的最后或中间名称(&#39; last&#39)来定义。 ;或者&#39;中间&#39;)匹配某个人在另一个村庄的姓或中间名。在这个例子中,居住在村庄1中的id = 1的个人在村庄2中有三个家庭成员(具有ids 7,9和10的家庭成员),因为他与他们共享至少一个姓名。然后,我想创建一个村庄的二元数据集,其中村庄之间的关系由这些村庄之间的家庭关系的数量来定义。因此,在此示例中,最终数据集将如下所示:

dyads<-data.table(v1='1',v2='2',ties=3+3+3+2+3+2)
dyads
   v1 v2 ties
1:  1  2   16

有没有一种有效的方法来计算这种关系&#39;数?我写了一个低效的循环来做这个,但我有一个庞大的数据集(在4万个村庄里约有5千万人)。

2 个答案:

答案 0 :(得分:6)

受Frank的回答启发的更新:

meltDT = 
  #use unique to eliminate last+middle duplication
  unique(melt(DT, measure.vars = c('last', 'middle'), 
              id.vars = c('village', 'id'), value.name = 'name'),
         by = c('village', 'id', 'name'))

#framework of output -- one row for each pair of villages
out.dt = with(DT, CJ(village, village, unique = TRUE))[V2 > V1]

setkey(meltDT, village)
setindex(meltDT, name)
#set indices to facilitate merges on names
out.dt[ , {
  ties := 
    #unique here eliminates matching on both last & middle
    uniqueN(meltDT[.(.BY$V1)][meltDT[.(.BY$V2)], on = 'name', 
                              allow.cartesian = TRUE, nomatch = 0L],
            by = c('id', 'i.id'))
}, by = .(V1, V2)]
out.dt
#    V1 V2 ties
# 1:  1  2   16

答案 1 :(得分:3)

这延伸到3个以上的村庄,但速度很慢:

DT = rbind(v1, v2)

matches = melt(DT, id="id", measure.vars=c("middle","last"))[, 
  CJ(id1 = id, id2 = id)[id1 < id2]
, by=value]

matches[DT, on=.(id1 = id), v1 := i.village ]
matches[DT, on=.(id2 = id), v2 := i.village ]

unique(matches[, !"value"])[v2 != v1, .N, by=.(v1, v2)]
#    v1 v2  N
# 1:  1  2 16

因此,它找到匹配的人(即使他们在同一个村庄),并且OP的期望结果只是使用这组匹配计算的汇总统计数据。