在日期范围内有效地加入 R 中的两个 data.tables(或 SQL 表)?
Efficiently joining two data.tables in R (or SQL tables) on date ranges?
我正在使用入院数据,并试图将 table 的观察结果联系起来:
dt_taken
patient_id
observation
value
2020-04-13 00:00:00
patient01
"Heart rate"
69
...
... 录取人数 table:
patient_id
admission_id
startdate
enddate
patient01
admission01
2020-04-01 00:04:20
2020-05-01 00:23:59
...
... 这样它 returns 与入院相关的观察列表,并拒绝所有在入院期间未进行的观察(错误记录,或在门诊就诊等):
dt_taken
admission_id
observation
value
2020-04-13 00:00:00
admission01
"Heart rate"
69
...
我迄今为止相对简单的方法是遍历每个患者,然后是每个患者的入院,然后是每个观察并将其分配给那个入院,但考虑到我有超过 36k 的入院和超过 100 万的观察,这是令人难以置信的时间-消费(我的政府发行的笔记本电脑因此讨厌我)。
有没有我遗漏的更有效的方法,要么使用 {data.table}(我必须承认我在这里绝对是个新手,更喜欢在 {tidyverse} 中工作)或者我什至可以运行 在存储 table 的 SQL 服务器上以拯救我老化的笔记本电脑?
data.table
对于 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 table。 (这是一个粗略的概括,主要针对左连接,因为这是 data.table::[
支持的所有内容;对于 inner/outer/full 连接,您将需要 merge(.)
或其他外部机制。参见 How to join (merge) data frames (inner, outer, left, right) and 以获得关于 JOIN 等的更多讨论)
由此可见,data.table::[
的机制是有效的
TABLE2[TABLE1, on = .(...)]
RHS[LHS, on = .(...)]
(意思是右边table实际上是从左到右第一个table...)
不等值连接输出中的名称是从 RHS 中保留下来的,看到没有找到 dt_taken
。 但是,那些startdate
和enddate
列的值来自dt_taken
.
正因为如此,我经常找到最简单的方法让我全神贯注于重命名和值,当我不确定时,我将一个连接列复制到一个新列中并使用该列,然后将其删除 post-merge。它草率而懒惰,但我发现自己错过了太多次,并认为这不是我想的那样。
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.table
和 POSIXt
,与 sqldf
一样好用,尽管常规 data.frame
也可以正常工作):
admissions <- setDT(structure(list(patient_id = "patient01", admission_id = "admission01", startdate = structure(1585713860, class = c("POSIXct", "POSIXt" ), tzone = ""), enddate = structure(1588307039, class = c("POSIXct", "POSIXt"), tzone = "")), class = c("data.table", "data.frame"), row.names = c(NA, -1L)))
observations <- setDT(structure(list(dt_taken = structure(1586750400, class = c("POSIXct", "POSIXt"), tzone = ""), patient_id = "patient01", observation = "Heart rate", value = 69L), class = c("data.table", "data.frame"), row.names = c(NA, -1L)))
(我用setDT
来修复我们这里不能传.internal.selfref
属性的问题。)
我正在使用入院数据,并试图将 table 的观察结果联系起来:
dt_taken | patient_id | observation | value |
---|---|---|---|
2020-04-13 00:00:00 | patient01 | "Heart rate" | 69 |
... |
... 录取人数 table:
patient_id | admission_id | startdate | enddate |
---|---|---|---|
patient01 | admission01 | 2020-04-01 00:04:20 | 2020-05-01 00:23:59 |
... |
... 这样它 returns 与入院相关的观察列表,并拒绝所有在入院期间未进行的观察(错误记录,或在门诊就诊等):
dt_taken | admission_id | observation | value |
---|---|---|---|
2020-04-13 00:00:00 | admission01 | "Heart rate" | 69 |
... |
我迄今为止相对简单的方法是遍历每个患者,然后是每个患者的入院,然后是每个观察并将其分配给那个入院,但考虑到我有超过 36k 的入院和超过 100 万的观察,这是令人难以置信的时间-消费(我的政府发行的笔记本电脑因此讨厌我)。
有没有我遗漏的更有效的方法,要么使用 {data.table}(我必须承认我在这里绝对是个新手,更喜欢在 {tidyverse} 中工作)或者我什至可以运行 在存储 table 的 SQL 服务器上以拯救我老化的笔记本电脑?
data.table
对于 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 table。 (这是一个粗略的概括,主要针对左连接,因为这是data.table::[
支持的所有内容;对于 inner/outer/full 连接,您将需要merge(.)
或其他外部机制。参见 How to join (merge) data frames (inner, outer, left, right) and 以获得关于 JOIN 等的更多讨论)由此可见,
data.table::[
的机制是有效的TABLE2[TABLE1, on = .(...)] RHS[LHS, on = .(...)]
(意思是右边table实际上是从左到右第一个table...)
不等值连接输出中的名称是从 RHS 中保留下来的,看到没有找到
dt_taken
。 但是,那些startdate
和enddate
列的值来自dt_taken
.正因为如此,我经常找到最简单的方法让我全神贯注于重命名和值,当我不确定时,我将一个连接列复制到一个新列中并使用该列,然后将其删除 post-merge。它草率而懒惰,但我发现自己错过了太多次,并认为这不是我想的那样。
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.table
和 POSIXt
,与 sqldf
一样好用,尽管常规 data.frame
也可以正常工作):
admissions <- setDT(structure(list(patient_id = "patient01", admission_id = "admission01", startdate = structure(1585713860, class = c("POSIXct", "POSIXt" ), tzone = ""), enddate = structure(1588307039, class = c("POSIXct", "POSIXt"), tzone = "")), class = c("data.table", "data.frame"), row.names = c(NA, -1L)))
observations <- setDT(structure(list(dt_taken = structure(1586750400, class = c("POSIXct", "POSIXt"), tzone = ""), patient_id = "patient01", observation = "Heart rate", value = 69L), class = c("data.table", "data.frame"), row.names = c(NA, -1L)))
(我用setDT
来修复我们这里不能传.internal.selfref
属性的问题。)