使用过滤来加速数据表的笛卡尔积

4
给定一个包含三个字段member_idprovider_idsrvc_dtdata.table。我需要计算一对提供者看到的不同成员数。如果访问发生在180天内,则认为成员已经访问了两个提供者。这用于使用访问阈值构建无向图并查找连接组件。
我正在使用Cartesian product with filter data.table中建议的方法。
我需要运行的实例有超过300万条记录,并且需要超过5分钟才能运行。是否有一种重新编写或新的data.table函数的方法,使其运行更快?
require(data.table)

nmem <- 5000
data.dt <- data.table(member_id=sample(10000:1000000,nmem,replace=TRUE), provider_id=sample(1000:2000,nmem,replace=TRUE),
    srvc_dt=sample(seq(as.Date('2014/01/01'), as.Date('2015/01/01'), by="day"), nmem, replace=TRUE))
setkey(data.dt, member_id)

prov_pair.dt <- data.dt[data.dt, {
        idx = provider_id<i.provider_id & abs(srvc_dt-i.srvc_dt)<180
        list(provider_id1 = provider_id[idx], 
            srvc_dt1 = srvc_dt[idx],
            provider_id2 = i.provider_id[any(idx)],
            srvc_dt2 = i.srvc_dt[any(idx)]
        )
    }, by=.EACHI, allow=TRUE]

prov_pair_agg.dt <- prov_pair.dt[, .(weight=length(unique(member_id))), .(provider_id1,provider_id2)]
2个回答

2

简单的左连接跟随过滤:

prov_pair.dt <- data.dt[data.dt,allow.cartesian=T][provider_id<i.provider_id &
  abs(srvc_dt-i.srvc_dt)<180,]

provider_id<i.provider_id可以避免对x,y和y,x的访问进行重复计数。

此外,在计算prov_pair_agg.dt时,请使用provider_idi.provider_id而不是provider_id1provider_id2

prov_pair_agg.dt <- prov_pair.dt[, .(weight=length(unique(member_id))), 
  .(provider_id,i.provider_id)]

在一台16GB内存的机器上,使用nmem=1,000,000,这个方法只需要1.487秒,而你目前使用的方法需要106.034秒。


请将nmem设置为1000000并尝试。我得到了以下错误...Join results in 2009362 rows; more than 2000000 = nrow(x)+nrow(i)... 。另一种方法的优点是它的内存效率更高(请参见@Arun帖子中的最后一行)。 - ironv
当nmem=1,000,000时,需要使用allow.cartesian=T,但只要有足够的内存,这种方式仍然很快。 - webb
1
也许可以通过筛选只看过一个提供者的成员来解决内存问题,即从 data.dt[, if (.N >= 2) .SD, by = member_id] 开始(当然这取决于数据)。 - eddi
@webb 谢谢。不幸的是,我一启动它后内存突然飙升,导致内存不足。我确实发现对于较小的实例来说,这要快得多。 - ironv
@eddi 谢谢。我已经在我的代码中提前过滤掉了它们。 - ironv

0

首先,将数据筛选出只包括看过一个以上提供者的成员:

res = data.dt[, if (.N >= 2) .SD, by = member_id]

接下来,添加日期的端点和提供者列的副本:

res[, `:=`(start.date = srvc_dt - 180,
           end.date   = srvc_dt + 180,
           provider2  = provider_id)]

最后,使用开发版本中提供的新的非等值连接:

res[res, on = .(member_id = member_id, provider2 < provider_id,
                srvc_dt < end.date, srvc_dt > start.date)
    , allow = T, nomatch = 0][, .N, by = .(provider1 = provider_id, provider2)]

注意:上面连接中的列名目前有点令人困惑,希望很快能够解决。如果上述内容不太清楚,您可以添加额外的列副本以了解具体情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接