在日期范围内有效地连接 R或 SQL 表中的两个 data.tables?

问题描述

我正在使用入院数据,并试图将观察表联系在一起:

dt_taken 患者 ID 观察 价值
2020-04-13 00:00:00 患者01 “心率” 69
...

... 附上一张录取名单:

患者 ID admission_id 开始日期 结束日期
患者01 admission01 2020-04-01 00:04:20 2020-05-01 00:23:59
...

...这样它会返回与入院相关的观察列表,并拒绝所有在入院期间未进行的观察(错误记录,或在门诊就诊等):

dt_taken admission_id 观察 价值
2020-04-13 00:00:00 admission01 “心率” 69
...

我迄今为止相对简单的方法是迭代每个患者,然后每个患者的入院,然后每个观察并将其分配给该入院,但鉴于我有 > 36k 入院和 > 100 万个观察,这非常耗时(我的政府发行的笔记本电脑因此讨厌我)。

有没有更有效的方法,我想念的,要么使用 {data.table}(我必须承认我是这里的绝对新手,更喜欢在 {tidyverse} 中工作),或者甚至我可以在 SQL 上运行存储表以保存我老化的笔记本电脑的服务器?

解决方法

数据表

对于 data.table,这主要是对 How to perform join over date ranges using data.table? 的欺骗,尽管它没有提供 RHS[LHS,on=.(..)] 方法。

observations
#              dt_taken patient_id observation value
# 1 2020-04-13 00:00:00  patient01  Heart rate    69
admissions
#   patient_id admission_id           startdate             enddate
# 1  patient01  admission01 2020-04-01 00:04:20 2020-05-01 00:23:59

### convert to data.table
setDT(observations)
setDT(admissions)

### we need proper 'POSIXt' objects
observations[,dt_taken := as.POSIXct(dt_taken)]
admissions[,(dates) := lapply(.SD,as.POSIXct),.SDcols = dates]

还有加入。

admissions[observations,on = .(patient_id,startdate <= dt_taken,enddate >= dt_taken)]
#    patient_id admission_id  startdate    enddate observation value
#        <char>       <char>     <POSc>     <POSc>      <char> <int>
# 1:  patient01  admission01 2020-04-13 2020-04-13  Heart rate    69

我认为有两点值得注意:

  • 在 SQL(以及其他对连接友好的语言中类似),它通常显示为

    select ...
    from TABLE1 left join TABLE2 ...
    

    建议 TABLE1 是 LHS(左侧),TABLE2 是 RHS 表。 (这是一个粗略的概括,主要面向左连接,因为这就是 data.table::[ 支持的全部内容;对于内部/外部/完全连接,您需要 merge(.) 或其他外部机制。请参阅 {{ 3}} 和 How to join (merge) data frames (inner,outer,left,right) 以了解有关 JOIN 等的更多讨论)

    由此,data.table::[的机制是有效的

    TABLE2[TABLE1,on = .(...)]
    RHS[LHS,on = .(...)]
    

    (意思是右边的表实际上是从左到右的第一个表...)

  1. inequi-joins 输出中的名称从 RHS 中保留,请参阅未找到 dt_taken但是,这些startdateenddate列的来自dt_taken

    因此,我经常找到最简单的方法来让我的大脑围绕重命名和值,例如当我不确定时,我将一个连接列复制到一个新列中并使用该列连接,然后在合并后将其删除。这是草率和懒惰,但我发现自己错过了太多次并认为这不是我所想的。

sqldf

如果 SQL 看起来更直观,这可能会更直接一些。

sqldf::sqldf(
  "select ob.*,ad.admission_id
   from observations ob
     left join admissions ad on ob.patient_id=ad.patient_id
         and ob.dt_taken between ad.startdate and ad.enddate")
#     dt_taken patient_id observation value admission_id
# 1 2020-04-13  patient01  Heart rate    69  admission01

数据(已经 data.tablePOSIXt,与 sqldf 一样好用,不过普通的 data.frame 也能正常工作):

admissions <- setDT(structure(list(patient_id = "patient01",admission_id = "admission01",startdate = structure(1585713860,class = c("POSIXct","POSIXt" ),tzone = ""),enddate = structure(1588307039,"POSIXt"),tzone = "")),class = c("data.table","data.frame"),row.names = c(NA,-1L)))
observations <- setDT(structure(list(dt_taken = structure(1586750400,patient_id = "patient01",observation = "Heart rate",value = 69L),-1L)))

(我使用 setDT 来修复我们无法在此处传递 .internal.selfref 属性的事实。)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...