【问题标题】:R: Matching on closest date (efficiency issue)R:匹配最近的日期(效率问题)
【发布时间】:2016-08-24 12:42:01
【问题描述】:
dt1 <- data.table(x = c("a", "a", "b", "b", "c"),
                  y = c("2016-03-01", "2016-05-10", "2016-04-14", "2016-06-25", "2016-01-12"))

   x          y
1: a 2016-03-01
2: a 2016-05-10
3: b 2016-04-14
4: b 2016-06-25
5: c 2016-01-12

dt2 <- data.table(x = c("a", "b", "b", "a"),
                  y = c("2016-05-13", "2016-04-16", "2016-06-20", "2016-02-28"),
                  z = c("1", "1", "2", "3"))

   x          y z
1: a 2016-05-13 1
2: b 2016-04-16 1
3: b 2016-06-20 2
4: a 2016-02-28 3

close.match <- function(dt1x, dt1y, threshold <= 3){
  if(dt1x %in% dt2$x){
    if(abs(as.numeric(as.Date(dt1y) - as.Date(dt2[x == dt1x][which.min(abs(as.Date(y) - as.Date(dt1y))),y]))) < threshold){
      return(dt2[x == dt1x][which.min(abs(as.Date(y) - as.Date(dt1y))),z])
    } else {
      "unknown"
    }
  } else {
    "unknown"
  }
}

dt1[,z:=dt1[,close.match(x,y),by=1:nrow(dt1)][,V1]]

   x          y       z
1: a 2016-03-01       3
2: a 2016-05-10       1
3: b 2016-04-14       1
4: b 2016-06-25 unknown
5: c 2016-01-12 unknown

想法是有两个事件dt1dt2,每个y 的时间戳为x,每个x 可以有多个条目,具有不同的时间戳。如果两个匹配 x 的事件在 3 天内发生,则预期的输出是将列 z 添加到 dt1,其值来自 dt2$z。否则返回“未知”。

上面的代码可以工作并且就是这样做的。但问题是矢量化——它的效率极低。希望找到有关如何以更有效的方式解决此类问题的任何想法。

【问题讨论】:

  • 使用ifelse矢量化?
  • 你为什么要按行运行as.Date??而且你每次都做6次?在进行任何计算之前,您应该做的第一件事是dt1[, y := as.IDate(y)] ; dt2[, y := as.IDate(y)]。其次,我会在这里尝试滚动加入
  • 可能是两个滚动连接。像indx1 &lt;- dt2[dt1, on = c(x = "x", y = "y"), roll = -3, which = TRUE] ; indx2 &lt;- dt2[dt1, on = c(x = "x", y = "y"), roll = 3, which = TRUE] ; dt1[!is.na(indx1), z := dt2[na.omit(indx1), z]] ; dt1[!is.na(indx2), z := dt2[na.omit(indx2), z]] ; dt1 这样的东西可以给你一些想法。或者试试foverlaps
  • 固定日期,它可能只产生很小的改进。并感谢有关 dt roll 参数/foverlaps 的指示。我会检查出来的。

标签: r date data.table matching


【解决方案1】:

如果您使用current development version of data.table, v1.9.7,那么您可以使用新的条件连接功能,如下所示:

# v1.9.7+
dt1[dt2, z := i.z, on=.(x, start<=y, end>=y)]

这一步是在将y 列都转换为Date 并将startend 单独添加到dt1 之后进行的。


有一个FR, #1639可以直接给on参数提供表达式,这样整个任务就可以完成如下:

dt1[dt2, z := i.z, on=.(x, y-3<=y, y+3>=y)]

我看看能不能加快速度。

【讨论】:

  • 对 Cran 的 1.9.7 何时发布有任何估计吗?我宁愿使用 1.9.6,因为我会分享我的代码,而宁愿使用非开发版本。
  • 重新审视了这个问题,因为data.table 1.9.8 在 Cran 上继续。它似乎工作得很好。
【解决方案2】:

感谢 David Arenburg,这是我想出的:

dt1 <- data.table(x = c("a", "a", "b", "b", "c"),
                  y = c("2016-03-01", "2016-05-10", "2016-04-14", "2016-06-25", "2016-01-12"))

dt2 <- data.table(x = c("a", "b", "b", "a"),
                  y = c("2016-05-13", "2016-04-16", "2016-06-20", "2016-02-28"),
                  z = c("1", "1", "2", "3"))

dt1[,y:=as.Date(y)]
dt2[,y:=as.Date(y)]

dt1[,start:=y-3]
dt1[,end:=y+3]

dt2[,start:=y]
dt2[,end:=y]

setkey(dt2, start, end)

dt1 <- foverlaps(dt1, dt2, type="any")
dt1 <- dt1[,.(x = i.x, y = i.y, z)]
dt1[is.na(z),z:="unknown"]
dt1

   x          y       z
1: a 2016-03-01       3
2: a 2016-05-10       1
3: b 2016-04-14       1
4: b 2016-06-25 unknown
5: c 2016-01-12 unknown

编辑:好的,虽然这有效。它在大型数据集上有点“爆炸”,相当快地达到 RAM 限制。仍然需要一些改进。

EDIT2:setkey 应该看起来像 setkey(dt2, x, start, end)。否则它将只查找数据集之间所有可能的时间间隔重叠,而不指定匹配变量。在两个数据集上都有超过 100k 的条目确保它过火了。

【讨论】:

  • 您是否尝试过 mu rolling join 解决方案?它应该是有效的内存和速度方面的。此外,Aruns 解决方案会更好。
  • 工作发生了。不能花更多时间研究不同的方法。一旦我找到一些时间,我也会用滚动加入来更新我的答案。再次感谢,您给出了正确的指示,问题解决了。
  • 您真的不需要研究...只需按原样在评论中尝试我的代码,看看它需要多长时间才能处理您的数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
  • 2022-06-15
  • 2018-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多