R 是否可以使用data.table(更快和/或更整洁)完成SQL非equi联接任务(下面的示例)?

R 是否可以使用data.table(更快和/或更整洁)完成SQL非equi联接任务(下面的示例)?,r,data.table,R,Data.table,我有一个包含3列的数据表:pid(作业)、starttime(开始时间)和fintime(结束时间),如下所示: require(data.table) dt <- data.table(pid=sample(1:100,100), starttime = sample(1:100,100)/100)[,fintime:=starttime + round(runif(100)/4,2)] require(data.table) dt所以这里是一个data.table方法,大约快20

我有一个包含3列的数据表:pid(作业)、starttime(开始时间)和fintime(结束时间),如下所示:

require(data.table)

dt <- data.table(pid=sample(1:100,100), starttime = sample(1:100,100)/100)[,fintime:=starttime + round(runif(100)/4,2)] 
require(data.table)

dt所以这里是一个data.table方法,大约快20倍,但是有一些注意事项(在末尾描述)

额外的记录似乎是合法的,所以问题是:
sqldf(…)
为什么找不到它们?我不能回答这个问题

第三,这适用于您的示例,但可能不容易用“更多约束”进行扩展

最后,这里是一个“基准”,其数据集与您的实际数据更为相似:

set.seed(1)
n <- 1e4   # more realistic example
dt <- data.table(pid=sample(1:n,n), 
                 starttime = sample(1:n,n)/n)[,fintime:=starttime + round(runif(n)/4,2)]
system.time(res.sql <- f.sql(dt))
#    user  system elapsed 
#   45.25    0.53   45.80 
system.time(res.DT  <- f.DT(dt))
#    user  system elapsed 
#    2.09    0.86    2.94 
set.seed(1)

n您可以尝试向一个或两个表添加索引,并检查这是否加快了SQL查询的速度。@G.Grothendieck,谢谢您的建议。对于更大的数据(当sql运行大约需要20秒时,初始观测值为10K),我做了:
sqldf(“在dt(starttime,fintime)上创建索引idx)”
并且没有显示出改进。这是因为每次调用
sqldf时,它都会用新数据库覆盖数据库。有关信息,请参见主页上的示例。
system.time(res
starttime
仅作为表达式的一部分出现,因此它无法确定如何使用索引。
fintime
应该是第一个,或者重新排列表达式,使
starttime
单独位于左侧。我们不能确定这是否有帮助,但可能足以允许它使用索引。jihoward,有一个quick look。看起来很棒!在处理过程中发现了一个bug。修复并测试了它。同时处理警告。到时候将更新此答案。@jihoward,这真是太神奇了!非常感谢!
额外的记录似乎是合法的,所以问题是:为什么sqldf没有找到它们(…)?
我检查了几个不匹配项,它们都位于边界上-它们应该限定“=”必须是一些数字表示的东西。修复了该错误。应该很快在1.9.6上可用。对于
=
>
,您可以添加/减去
。机器$double.eps
开始/结束间隔,但它并不简单。我们将看看我们能做些什么。+1。
require(data.table)
set.seed(1)  # for reproducible example
n <- 100     # simple example
dt <- data.table(pid=sample(1:n,n), 
                 starttime = sample(1:n,n)/n,2)[,fintime:=starttime + round(runif(n)/4,2)]
# sqldf approach
require(sqldf)
f.sql <- function(dt) {
  sqldf("create index idx on dt(starttime,fintime)")
  res <- sqldf("select a.pid as first, b.pid as second , a.starttime as starttime,  b.fintime as fintime 
    from  dt a, dt b 
    where b.starttime >= a.fintime + 0.05
      and b.starttime <= a.fintime + 0.4
  ")  
}
res.sql <- f.sql(dt)

# data.table approach with foverlaps(...): need >= 1.9.4 for this!!
packageVersion("data.table")
# [1] ‘1.9.4’
f.DT <- function(dt) {
  lookup <- dt[,list(second=pid, fintime, a=starttime,b=starttime)]
  setkey(lookup,a,b)
  DT      <- dt[,list(first=pid, starttime, a=fintime+0.05,b=fintime+0.4)]
  J.olaps <- foverlaps(DT,lookup,type="any",nomatch=0) 
  J.olaps[,list(first,second,starttime,fintime)]
}
res.DT <- f.DT(dt)
indx  <- data.table(first=res.sql$first,second=res.sql$second,key=c("first","second"))
setkey(res.DT,first,second)
extra <-  res.DT[!indx,]
set.seed(1)
n <- 1e4   # more realistic example
dt <- data.table(pid=sample(1:n,n), 
                 starttime = sample(1:n,n)/n)[,fintime:=starttime + round(runif(n)/4,2)]
system.time(res.sql <- f.sql(dt))
#    user  system elapsed 
#   45.25    0.53   45.80 
system.time(res.DT  <- f.DT(dt))
#    user  system elapsed 
#    2.09    0.86    2.94