按日期连接两个 data.table,表 1 中最接近的日期严格小于第二个表中的日期

Mee*_*eep 4 r data.table

从 SO 上的其他地方窃取一个虚拟示例(在确切日期加入 data.table,或者如果不是在最近的小于日期的情况下加入)),我希望根据严格更早的第一个日期(Dt1 中的日期)加入两个表比第二个日期(Dt2 中的日期)。

还关闭了 DataCombine 解决方案“幻灯片”功能中的“警告”消息,因为它可能不公平地减慢了 mtotos 解决方案的速度。

library(data.table)

Dt1 <- read.table(text="
date      x
1/26/2010,  10  
1/25/2010,  9  
1/24/2010,  9   
1/22/2010,  7    
1/19/2010,  11", header=TRUE, stringsAsFactors=FALSE)

Dt2 <- read.table(text="
date
1/26/2010   
1/23/2010   
1/20/2010", header=TRUE, stringsAsFactors=FALSE)
Run Code Online (Sandbox Code Playgroud)

加入所需的结果

   date     x  
1/26/2010 - 9 # based on closest observation strictly less than date  
1/23/2010 - 7   
1/20/2010 - 11
Run Code Online (Sandbox Code Playgroud)

两种解决方案的时间

(我保留 data.frame 格式用于输入 mtoto 的解决方案,而 data.table 则用于 jangorecki 的解决方案)。

solution.mtoto = function(Df1, Df2)
{
  #Full outer join of two df's
  merged <- merge(Df1, Df2, by = "date", all = T, sort=T)

  # Shifting values backwards by one using 'slide' from DataCombine
  merged <- slide(merged, Var = "x", slideBy = -1, reminder = F)

  # Inner join retaining the relevant cols
  return(merge(Df2,merged)[,-2])
}

solution.jangorecki = function(Dt1, Dt2)
{
  offset.roll.join = function(Dt1, Dt2){
    Dt2[, jndate := date - 1L] # produce join column with offset
    on.exit(Dt2[, jndate := NULL]) # cleanup join col on exit
    Dt1[Dt2, .(date = i.date, x), on = c("date" = "jndate"), roll = Inf] # do rolling join
  }
  return(offset.roll.join(Dt1, Dt2))
}

res.mtoto = sapply(1:10, FUN = function(x){system.time({solution.mtoto(Df1, Df2)})})

res.jangorecki = sapply(1:10, FUN = function(x){system.time({solution.jangorecki(Dt1, Dt2)})})


> res.mtoto[c("user.self", "sys.self"),]
           [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
user.self 0.004 0.004 0.004 0.004 0.003 0.003 0.003 0.003 0.003 0.003
sys.self  0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000

> res.jangorecki[c("user.self", "sys.self"),]
           [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
user.self 0.005 0.005 0.004 0.004 0.005 0.004 0.004 0.004 0.003 0.004
sys.self  0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
Run Code Online (Sandbox Code Playgroud)

编辑,在 mtoto 的解决方案中意外引用了 Dt1 而不是 Df1。现在已修复。

相似的速度(在更大的数据集上可能更明显?)。我的另一个问题是我希望在第二个表中返回日期。

例如,期望的结果是:

date - x - date2
1/26/2010 - 9 - 1/25/2010
1/23/2010 - 7 - 1/22/2010
1/20/2010 - 11 - 1/19/2010
Run Code Online (Sandbox Code Playgroud)

jan*_*cki 5

带偏移的滚动连接-1L

2016-04-02 更新:通过当前开发 v1.9.7 中的此提交,无需创建临时列即可完成此操作。来自新闻

x 的列可以随时j使用前缀来引用。x.当需要将 x 的列同时作为连接列时,这尤其有用。这是一个解决#1615的补丁。

Dt2[, jndate := date - 1L]
Dt1[Dt2,
    .(date = i.date, orgdate = x.date, x),
    on = c("date" = "jndate"),
    roll = Inf]
#         date    orgdate  x
#1: 2010-01-26 2010-01-25  9
#2: 2010-01-23 2010-01-22  7
#3: 2010-01-20 2010-01-19 11
Run Code Online (Sandbox Code Playgroud)

原始答案,如果您使用的是 1.9.6 或更早版本,则很有用。

library(data.table)

# data
Dt1 = fread("date      x
1/26/2010,  10  
1/25/2010,  9  
1/24/2010,  9   
1/22/2010,  7    
1/19/2010,  11")[, date := as.IDate(date, format=("%m/%d/%Y"))][]
Dt2 = fread("date
1/26/2010   
1/23/2010   
1/20/2010")[, date := as.IDate(date, format=("%m/%d/%Y"))][]

# solution
offset.roll.join = function(Dt1, Dt2){
    Dt2[, jndate := date - 1L] # produce join column with offset
    Dt1[, orgdate := date] # should not be needed after data.table#1615
    on.exit({Dt2[, jndate := NULL]; Dt1[, orgdate := NULL]}) # cleanup on exit
    Dt1[Dt2, .(date = i.date, orgdate, x), on = c("date" = "jndate"), roll = Inf] # do rolling join
}
offset.roll.join(Dt1, Dt2)
#         date    orgdate  x
#1: 2010-01-26 2010-01-25  9
#2: 2010-01-23 2010-01-22  7
#3: 2010-01-20 2010-01-19 11
Run Code Online (Sandbox Code Playgroud)

  • @Meep Arun 做了很好的改进,可以简化查询。 (2认同)