【问题标题】:Join two data.tables on date, with closest date in table 1 strictly less than date in second table在日期上加入两个 data.tables,表 1 中最接近的日期严格小于第二个表中的日期
【发布时间】:2016-02-13 12:32:14
【问题描述】:

从 SO (Join data.table on exact date or if not the case on the nearest less than date) 的其他地方窃取了一个虚拟示例,我希望根据第一个日期(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)

加入的期望结果

   date     x  
1/26/2010 - 9 # based on closest observation strictly less than date  
1/23/2010 - 7   
1/20/2010 - 11

两种解决方案的时间

(我为 mtoto 的解决方案保留 data.frame 格式,为 jangorecki 保留 data.table 格式)。

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

编辑,在 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

【问题讨论】:

  • 您可以从date 中减去整数1 并继续滚动连接,如链接问题中一样,或者将1 添加到第二张表的date 中。
  • 完全外连接可能非常昂贵,这取决于更大集合中的差距有多大。仅几行计时不会显示任何内容。

标签: r data.table


【解决方案1】:

带有-1L 偏移量的滚动连接。

2016-04-02 更新:在当前开发版本 v1.9.7 中使用 this commit,无需创建临时列即可完成此操作。来自NEWS

j 中可以始终使用前缀x. 引用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

原始答案,如果您使用的是 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

【讨论】:

  • 这看起来很棒!我想知道,在结果集中返回与 dt1 匹配的日期是否简单?
  • 只需将.(date = i.date, x) 更改为.(date = i.date, date2 = date, x)。如果您能报告您的数据中两个答案的时间,我会很高兴。最简单的方法是将代码包装成system.time({...})
  • 我会将时间添加到我的主要问题中。但是我试过你刚才提供的解决方案,它并没有给我第二次约会?我可能会将此作为问题的一部分添加...
  • @Meep 这不是在已回答问题时更改问题的最佳做法,但您可以这样做
  • @Meep Arun 做了很好的改进,可以简化查询。
【解决方案2】:

分三步:

library(DataCombine)

#Full outer join of two df's
merged <- merge(Dt1, Dt2, by = "date", all = T)

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

# Inner join retaining the relevant cols
merge(Dt2,merged)[,-2]
#       date x-1
#1 1/20/2010  11
#2 1/23/2010   7
#3 1/26/2010   9

【讨论】:

  • 感谢您的贡献。没听说过这个包。不幸的是,我的“真实”data.frame 有 500,000 行,所以完整的外连接将非常昂贵!
猜你喜欢
  • 2018-04-16
  • 1970-01-01
  • 2014-12-12
  • 2019-09-21
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 2020-11-13
  • 2015-07-24
相关资源
最近更新 更多