【问题标题】:R: Finding the closest of two dates according to an index columnR:根据索引列查找两个日期中最接近的日期
【发布时间】:2019-10-16 20:00:09
【问题描述】:

该项目的目的是测量医学实验室检查和最近一次服药之间的时间间隔。每个患者的后续剂量不同,每个受试者的后续实验室检查次数也不同。

第一个数据框包含 study_id 和相应的剂量日期:

library(dplyr)
library(lubridate)

study_id<- c(1, 1, 1, 2, 2, 3)
dose_dt <- c('1/1/00', '2/1/00', '3/1/00', '1/1/01', '2/1/01', '1/1/02')
doses_df <- data.frame(study_id, dose_dt)
doses_df$dose_dt <- mdy(doses_df$dose_dt)
print(doses_df)

 study_id    dose_dt
1        1 2000-01-01
2        1 2000-02-01
3        1 2000-03-01
4        2 2001-01-01
5        2 2001-02-01
6        3 2002-01-01

第二个数据框具有匹配的 study_id,这次是实验室检查日期和相关的实验室值以及异常指示符(是/否)。

study_id <- c(1, 1, 1, 2, 3, 3, 3)
lab_dt <- c('1/1/99', '3/1/00', '4/1/00', '2/1/01', '2/1/02', '3/1/02', '4/1/02')
lab_result <- c(100, 200, 50, 25, 75, 100, 75)
lab_abn_yn <- c(0, 0, 1, 1, 1, 0, 1)
labs_df <- data.frame(study_id, lab_dt, lab_result, lab_abn_yn)
labs_df$lab_dt <- mdy(labs_df$lab_dt)
print(labs_df)

  study_id     lab_dt lab_result lab_abn_yn
1        1 1999-01-01        100          0
2        1 2000-03-01        200          0
3        1 2000-04-01         50          1
4        2 2001-02-01         25          1
5        3 2002-02-01         75          1
6        3 2002-03-01        100          0
7        3 2002-04-01         75          1

请注意,受试者 1 的一次实验室检查是在第一次服药之前的过去,受试者 2 的剂量多于实验室检查,受试者 3 的剂量少于实验室检查。

我希望 R 在实验室检查之前确定最近服药的日期,以便我可以计算服药和实验室检查之间的时间间隔。输出将保留实验室值和指标。最好,在第一次给药之前的实验室检查(从剂量到实验室检查的负时间间隔)将报告为 NA,但我可以轻松过滤掉负时间间隔。我也知道如何使用 lubridate 来计算时间间隔,所以需要将其添加到解释中。

期望的输出:

  study_id lab_dt     most_recent_dose_dt lab_result lab_abn_yn interval_months
     <dbl> <chr>      <chr>               <chr>           <dbl>           <dbl>
1        1 1999-01-01 NA                  NA                 NA          NA    
2        1 2000-03-01 2000-02-02          200                 0           0.966
3        1 2000-04-01 2000-03-01          50                  1           1    
4        2 2001-02-01 2001-01-01          25                  1           1    
5        3 2002-02-01 2002-01-01          75                  1           1    
6        3 2002-03-01 2002-01-01          100                 0           2    
7        3 2002-04-01 2002-01-01          75                  1           3      

我尝试了许多合并方案,但没有一个能保留所有数据。有大约 40,000 个主题,因此手动执行此操作是不可行的。非常感谢任何帮助。

【问题讨论】:

    标签: r date datetime


    【解决方案1】:

    有一个使用data.table 的单行解决方案,使用non-equi 连接:

    library(data.table)
    # create data.tables
    labs_df <-  setDT(labs_df)
    doses_df <- setDT(doses_df)
    
    # create join variable
    doses_df[,join_time := dose_dt]
    labs_df[,join_time := lab_dt]
    
    # do nonequi join with a condition
    doses_df[labs_df,on=.(study_id,join_time < join_time),mult = "last"]
    
       study_id    dose_dt  join_time     lab_dt lab_result lab_abn_yn
    1:        1       <NA> 1999-01-01 1999-01-01        100          0
    2:        1 2000-02-01 2000-03-01 2000-03-01        200          0
    3:        1 2000-03-01 2000-04-01 2000-04-01         50          1
    4:        2 2001-01-01 2001-02-01 2001-02-01         25          1
    5:        3 2002-01-01 2002-02-01 2002-02-01         75          1
    6:        3 2002-01-01 2002-03-01 2002-03-01        100          0
    7:        3 2002-01-01 2002-04-01 2002-04-01         75          1
    

    这里的想法是,您要在labs_df 的study_id 和join_time 上合并doses_df,它们尊重来自dose_df 的join_time 条件joint_time。

    我创建了一个join_time 列,因为连接只保留两个时间列中的一个,否则会更改名称,所以我总是感到困惑:如果你直接这样做

    doses_df[labs_df,on=.(study_id,dose_dt < lab_dt),mult = "last"]
    

    它给你

       study_id    dose_dt lab_result lab_abn_yn
    1:        1 1999-01-01        100          0
    2:        1 2000-03-01        200          0
    3:        1 2000-04-01         50          1
    4:        2 2001-02-01         25          1
    5:        3 2002-02-01         75          1
    6:        3 2002-03-01        100          0
    7:        3 2002-04-01         75          1
    

    这对于 lab_result 和其他列是正确的,但对于 dose_dt 列来说却很混乱,因为它变成了您在其上进行合并的 lab_dt 列(合并就像子设置 doses_dt 列lab_dt 值)。

    我实际上想在一开始就使用滚动连接:

    doses_df[labs_df,on=.(study_id,join_time),roll = T]
    
       study_id    dose_dt  join_time     lab_dt lab_result lab_abn_yn
    1:        1       <NA> 1999-01-01 1999-01-01        100          0
    2:        1 2000-03-01 2000-03-01 2000-03-01        200          0
    3:        1 2000-03-01 2000-04-01 2000-04-01         50          1
    4:        2 2001-02-01 2001-02-01 2001-02-01         25          1
    5:        3 2002-01-01 2002-02-01 2002-02-01         75          1
    6:        3 2002-01-01 2002-03-01 2002-03-01        100          0
    7:        3 2002-01-01 2002-04-01 2002-04-01         75          1
    

    但问题是它保持日期等于或低于。

    我使用this question 找到了等值连接解决方​​案,我推荐this tutorial 用于滚动连接。 data.table 速度很快,并且允许您在一行中执行您真正想要的操作(取符合 dose_dt &lt; lab_dt 的合并中的最后一行)。

    【讨论】:

    • 再次感谢,也感谢您的介绍,C
    • No pb,实际上我必须对一个月前的同类数据(患者访问和实验室结果)做同样的事情,所以我仍然记忆犹新,很高兴解释它.
    【解决方案2】:

    我们可以分三步完成:

    1. 过滤以仅保留实验室日期之后发生的剂量
    2. 过滤以仅保留每个实验室日期的最新剂量(由于第一个过滤器,现在是最近的剂量)
    3. 重新加入实验室列表以重新包括之前没有剂量的实验室日期

    请注意,您的示例数据与打印出来的数据不完全一样,因为第二次观察,2 月 1 日变成了 2 月 2 日。

    library(tidyverse)
    library(lubridate)
    doses_df <- tibble(
      study_id = c(1, 1, 1, 2, 2, 3),
      dose_dt = mdy(c("1/1/00", "2/1/00", "3/1/00", "1/1/01", "2/1/01", "1/1/02"))
    )
    labs_df <- tibble(
      study_id = c(1, 1, 1, 2, 3, 3, 3),
      lab_dt = mdy(c("1/1/99", "3/1/00", "4/1/00", "2/1/01", "2/1/02", "3/1/02", "4/1/02")),
      lab_result = c(100, 200, 50, 25, 75, 100, 75),
      lab_abn_yn = c(0, 0, 1, 1, 1, 0, 1)
    )
    
    most_recent_doses <- labs_df %>%
      left_join(doses_df, by = "study_id") %>%
      group_by(study_id, lab_dt) %>%
      filter(dose_dt < lab_dt) %>%
      filter(dose_dt == max(dose_dt)) %>%
      select(study_id, lab_dt, dose_dt)
    
    labs_df %>%
      left_join(most_recent_doses, by = c("study_id", "lab_dt")) %>%
      mutate(interval_months = interval(dose_dt, lab_dt) / months(1))
    #> # A tibble: 7 x 6
    #>   study_id lab_dt     lab_result lab_abn_yn dose_dt    interval_months
    #>      <dbl> <date>          <dbl>      <dbl> <date>               <dbl>
    #> 1        1 1999-01-01        100          0 NA                      NA
    #> 2        1 2000-03-01        200          0 2000-02-01               1
    #> 3        1 2000-04-01         50          1 2000-03-01               1
    #> 4        2 2001-02-01         25          1 2001-01-01               1
    #> 5        3 2002-02-01         75          1 2002-01-01               1
    #> 6        3 2002-03-01        100          0 2002-01-01               2
    #> 7        3 2002-04-01         75          1 2002-01-01               3
    

    由reprex package (v0.3.0) 于 2019 年 10 月 16 日创建

    【讨论】:

    • 我非常喜欢dplyr,但我发现data.table 更强大
    • 非 equi 连接是 dplyr 所没有的长期功能,当然。但是对于生成两个数据集的乘积不会令人望而却步的常见小数据情况,我发现认识到无需此类连接即可实现所需结果很有用。 data.table 对于数百万行总是很好:)
    • 完美,谢谢!我忽略的是过滤之前的绝地双组_by。我总是忘记多条目 group_by 的实用性。还要感谢丹尼斯。我在想滚动连接可能会在这个问题的未来出现,但我不太清楚如何实现它。谢谢你们俩。这对于这个试图理解大规模(IRB 批准的)EMR 数据转储的项目将非常有帮助。干杯,凯西
    • @CalumYou 我完全同意你的观点,实际上我花了很多时间来正确理解这个加入,当我认为你提出的 dplyr 方法更容易理解时。
    猜你喜欢
    • 1970-01-01
    • 2019-04-28
    • 1970-01-01
    • 2013-06-18
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    相关资源
    最近更新 更多