R使用data.table进行多个条件连接

时间:2014-02-05 15:37:34

标签: r data.table

我有一个大型数据集和一个查找表。我需要为数据集中的每一行返回查找中满足条件的行的最小值。

考虑到我的数据集的大小,我不愿意通过交叉连接一起破解一个iffy解决方案,因为这将创建数百万条记录。我希望有人可以建议一个(理想情况下)利用base r或data.table的解决方案,因为它们已经以有效的方式使用。

示例

A<-seq(1e4,9e4,1e4)
B<-seq(0,1e4,1e3)

dt1<-data.table(expand.grid(A,B),ID=1:nrow(expand.grid(A,B)))
setnames(dt1, c("Var1","Var2"),c("A","B"))

lookup<-data.table(minA=c(1e4,1e4,2e4,2e4,5e4),
                 maxA=c(2e4,3e4,7e4,6e4,9e4),
                 minB=rep(2e3,5),
                 Val=seq(.1,.5,.1))

# Sample  Desired Value
     A     B    ID Val
99: 90000 10000 99 0.5

在SQL中,我会按照

的方式写一些内容
SELECT ID, A, B, min(Val) as Val
FROM dt1
LEFT JOIN lookup on dt1.A>=lookup.minA
                 and dt1.A<=lookup.maxA
                 and dt1.B>=lookup.minB
GROUP BY ID, A, B

哪个会将所有匹配的记录从lookup加到dt1并返回最小的Val

更新

到目前为止,我的解决方案如下:

CJ.table<-function(X,Y) setkey(X[,c(k=1,.SD)],k)[Y[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]

dt1.lookup<- CJ.table(dt1,lookup)[A>=minA & A<=maxA & B>=minB,
                                  list(Val=Val[which.min( Val)]),
                                  by=list(ID,A,B)]
dt1.lookup<-rbind.fill(dt1.lookup, dt1[!ID %in% dt1.lookup$ID])

这将检索所有记录,并允许在需要时从查找表中返回其他列。它还具有强制选择最小Val的好处。

2 个答案:

答案 0 :(得分:1)

我发现没有交叉加入的解决方案首先需要通过删除AB完全超出范围的行来准备数据:

Prep = dt1[A >= min(lookup$minA) & A <= max(lookup$maxA) & B >= min(lookup$minB)]

然后,您创建一个数据表,其中列出了与最低Val对应的每个条件:

Indices = Prep[,list(min(which(A >= lookup$minA)), 
                     min(which(A <= lookup$maxA)), 
                     min(which(B >= lookup$minB)), A, B),by=ID]

然后你必须在满足所有三个条件的最低点获得Val

Indices[,list(Val=lookup$Val[max(V1,V2,V3)], A, B),by=ID]

看看这是否能满足您的需求:

   ID Val     A     B
 1: 19 0.1 10000  2000
 2: 20 0.1 20000  2000
 3: 21 0.2 30000  2000
 4: 22 0.3 40000  2000
 5: 23 0.3 50000  2000
 6: 24 0.3 60000  2000
 7: 25 0.3 70000  2000
 8: 26 0.5 80000  2000
 9: 27 0.5 90000  2000
10: 28 0.1 10000  3000

答案 1 :(得分:1)

我的第一个想法是试图制作一个像Senor O这样的指数。然而,min(Val)使得索引表更加难以让我思考。我想这样做的方法是遍历查找表。

dt1[,Val:=as.numeric(NA)]
for (row in 1:NROW(lookup)) {
  dt1[A>=lookup[order(Val)][row,minA]&A<=lookup[order(Val)][row,maxA]&B>=lookup[order(Val)][row,minB]&is.na(Val),Val:=lookup[order(Val)][row,Val]]
  }

我认为这应该有效,因为它首先使用NA值设置新列。

然后它按Val顺序放置查找表,这样你就可以得到它的最低值。

在每个循环中,如果dt1中的值仍为NA中的Val且我们正在循环{{1}按照最小lookup到最大的顺序,它将确保您获得所需的Val

替换rbind.fill行
min(Val)

它将消除对rbindlist(list(dt1.lookup,dt1[!ID %in% dt1.lookup[,ID]][,list(ID, A, B, Val=as.numeric(NA))])) 包的依赖,我认为它会更快。