在 n 天内找到 n 个重叠的日期

Find n overlapping dates within n number of days

我正在寻找在 90 天内在一个组中发生的 >=4 个独特事件,然后标记 ID。

只是一个测试例子:

library(dplyr)

set.seed(1)

test <- data.frame(
  PATID = sample(1:1e4, 1e5, replace = TRUE),
  PROV = sample(1:50, 1e5, replace = TRUE),
  GROUP = sample(0:1, 1e5, replace = TRUE),
  DATE = as.Date(sample(
    as.Date("2020-01-01"):as.Date("2020-12-31"),
    1e5,
    replace = TRUE
  ), origin = "1970-01-01")
)

如果我们查看 PATID==5,我们可以看到有 4 个独特的 PROV 日期在 90 天内并且在我们感兴趣的组中,因此应该被标记。

> test %>% filter(PATID==5) %>% arrange(GROUP,DATE)
   PATID PROV GROUP       DATE
1      5    2     0 2020-05-07
2      5    3     0 2020-05-20
3      5    3     0 2020-11-15
4      5   49     0 2020-12-14
5      5   45     1 2020-02-16
6      5   50     1 2020-03-19
7      5   38     1 2020-03-25
8      5   27     1 2020-03-29
9      5   42     1 2020-08-30
10     5   46     1 2020-11-03
11     5   25     1 2020-11-13
12     5   29     1 2020-12-26
> as.Date("2020-03-29")-as.Date("2020-02-16")<=90
[1] TRUE

最终,我正在寻找 90 天内 GROUP==1GROUP==0 的比例 >=4 个唯一 PROVs。理想情况下,我更喜欢使用 data.table 仅仅是因为数据的规模。

正在尝试一些代码:

test %>%
  filter(PATID %in% 1:5) %>%
  group_by(PATID,GROUP) %>%
  arrange(GROUP, DATE) %>%
  mutate(lag = DATE - lag(DATE),
         day_count = case_when(lag <= 90 ~ TRUE,
                               is.na(lag) ~ TRUE,
                               TRUE ~ FALSE)) %>%
  mutate(crit = cumsum_reset(day_count)) %>% 
  ungroup() %>%
  group_by(PATID) %>%
  mutate(flag = case_when(max(crit) >= 4 ~ 1,
                          TRUE ~ 0)) %>% 
  arrange(PATID)

越来越近了,只需要整理出 90 window 而不是粗略地测试每个日期是否在 90 天内。

也许以下就是您所追求的。请检查逻辑是否符合您的意思。我留下了比必要更明确的内容,以便更容易理解这个想法。主要思想是,如果在排序后有来自同一个 PATDI 和 GROUP 的观测值在第三个滞后 diff_3 := DATE - shift(DATE, 3) 的 90 天内,那么它应该被标记。这是通过检查 diff_check = diff_3<=90 完成的。如果标记了任何 PATID/GROUP 的任何观察,则在仅按 PATID 分组后,相应的 ID 将由 keep = max(diff_check, na.rm = TRUE, pmin = 0) 标记。

  • 使用第三个滞后来解释 4 个或更多但严格来说不超过 4 个观测值。
  • 总而言之,它有意义吗?
library(data.table)
set.seed(1)
test <- data.frame(
  PATID = sample(1:1e4, 1e5, replace = TRUE),
  PROV = sample(1:50, 1e5, replace = TRUE),
  GROUP = sample(0:1, 1e5, replace = TRUE),
  DATE = as.Date(sample(
    as.Date("2020-01-01"):as.Date("2020-12-31"),
    1e5,
    replace = TRUE
  ), origin = "1970-01-01")
)
test %>% filter(PATID==5) %>% arrange(GROUP,DATE)
#> Error in test %>% filter(PATID == 5) %>% arrange(GROUP, DATE): could not find function "%>%"

dt <- as.data.table(test)
dt <- dt[order(PATID, GROUP, DATE)]
dt[, diff_3 := DATE - shift(DATE, 3), by = c("PATID", "GROUP")]

# check amount of unique values of PROV in previous 4 observations
dt[, unique_last_4 := frollapply(x = PROV, n = 4, FUN = uniqueN), by = c("PATID", "GROUP")]

# check if within 90 days and unique PROVs 
dt[, diff_check := diff_3<=90 & unique_last_4==4, by = c("PATID", "GROUP")]

# final check to flag all observations of ID that satisfied at least once the above checks
dt[, to_keep := max(diff_check, na.rm = TRUE, pmin = 0), by = "PATID"]
# NOTE: unsure if you mean to group only by PATID here or by PATID & GROUP.

head(dt[to_keep==1], 20)
#>     PATID PROV GROUP       DATE   diff_3 unique_last_4 diff_check to_keep
#>  1:     5    2     0 2020-05-07  NA days            NA         NA       1
#>  2:     5    3     0 2020-05-20  NA days            NA         NA       1
#>  3:     5    3     0 2020-11-15  NA days            NA         NA       1
#>  4:     5   49     0 2020-12-14 221 days             3      FALSE       1
#>  5:     5   45     1 2020-02-16  NA days            NA         NA       1
#>  6:     5   50     1 2020-03-19  NA days            NA         NA       1
#>  7:     5   38     1 2020-03-25  NA days            NA         NA       1
#>  8:     5   27     1 2020-03-29  42 days             4       TRUE       1
#>  9:     5   42     1 2020-08-30 164 days             4      FALSE       1
#> 10:     5   46     1 2020-11-03 223 days             4      FALSE       1
#> 11:     5   25     1 2020-11-13 229 days             4      FALSE       1
#> 12:     5   29     1 2020-12-26 118 days             4      FALSE       1
#> 13:     7    1     0 2020-04-10  NA days            NA         NA       1
#> 14:     7   44     0 2020-04-29  NA days            NA         NA       1
#> 15:     7   27     0 2020-05-05  NA days            NA         NA       1
#> 16:     7   41     0 2020-06-11  62 days             4       TRUE       1
#> 17:     7   35     0 2020-06-30  62 days             4       TRUE       1
#> 18:     7   11     0 2020-12-18 227 days             4      FALSE       1
#> 19:     7   24     1 2020-12-24  NA days            NA         NA       1
#> 20:     7   13     1 2020-12-29  NA days            NA         NA       1

reprex package (v2.0.0)

于 2021-06-22 创建

dplyr 版本

test_keep <- test %>% arrange(PATID, GROUP, DATE) %>%
  head(1000) %>% # otherwise it takes too long in my pc, which shows data.table's efficiency! 
  group_by(PATID, GROUP) %>%
  mutate(diff_3 = DATE - lag(DATE, 3),
         diff_check = diff_3<=90, 
         unique_last_4 = frollapply(x = PROV, n = 4, FUN = uniqueN)
  ) %>% group_by(PATID) %>%
  mutate(keep = max(diff_check, na.rm = TRUE, pmin = 0)) %>%
  arrange(PATID, GROUP)

test_keep %>% filter(keep==1) %>% head(20)

问题中有些歧义,所以这可能不太正确。我尝试使用 dplyr 和本地数据帧执行此操作,但自连接导致溢出(100,000 乘以 100,000)。 使用 data.table 和使用具有 OVERLAPS 函数的 PostgreSQL 似乎可以工作。 (请注意,我使用小写的变量名称使 SQL 更容易使用。)

在下面的答案中,我从患者就诊((patid, prov, group, date) 组合)开始,并期待 90 天来捕获该患者(patid)对 other 的所有就诊 个提供商(prov != prov_other)。然后我计算该前瞻期内不同提供者的数量(当没有访问时,这将是 NA,就像查看样本中患者的最后一次访问时一样)。然后,我计算在随后的 90 天内 其他 不同提供者的数量为 3 或更多的访问次数。

最后,我按 (group, year) 分组并计算在随后的 90 天内访问至少三个其他提供者的访问比例。鉴于生成数据的方式,两组在这个指标上看起来相似也就不足为奇了。

请注意,每次患者就诊在这里构成一个观察单位。在实践中,在计算统计数据或进行某种其他类型的聚合之前通过(比如)(patid, year) 进行聚合可能是有意义的。

library(data.table)
library(dplyr, warn.conflicts = FALSE)

set.seed(1)

test <- tibble(
    patid = sample(1:1e4, 1e5, replace = TRUE),
    prov = sample(1:50, 1e5, replace = TRUE),
    group = sample(0:1, 1e5, replace = TRUE),
    date = as.Date(sample(
        as.Date("2020-01-01"):as.Date("2020-12-31"),
        1e5,
        replace = TRUE
    ), origin = "1970-01-01")) %>%
    as.data.table()
test 
#>         patid prov group       date
#>      1:  1017    6     1 2020-08-03
#>      2:  8004   34     0 2020-12-15
#>      3:  4775   32     0 2020-06-21
#>      4:  9725   47     1 2020-09-25
#>      5:  8462   15     0 2020-03-05
#>     ---                            
#>  99996:   949   47     0 2020-07-05
#>  99997:  2723   37     0 2020-08-18
#>  99998:   201   27     1 2020-01-06
#>  99999:   163    9     0 2020-03-06
#> 100000:  3204   48     1 2020-11-17

df_overlap <- 
    test %>% 
    inner_join(test, by = "patid", suffix = c("", "_other")) %>%
    filter(prov != prov_other) %>%
    filter(date_other >= date & date_other <= date + 90L)

mt_4_provs_df <-
    df_overlap %>% 
    group_by(patid, prov, group, date) %>%
    summarize(n_providers = n_distinct(prov_other), .groups = "drop")

results <- 
    test %>%
    left_join(mt_4_provs_df, by = c("patid", "prov", "group", "date")) %>%
    mutate(mt_4_provs = n_providers >= 3,
           year = year(date)) %>%
    group_by(group, year) %>%
    summarize(prop_mt_4_provs = mean(mt_4_provs, na.rm = TRUE),
              .groups = "drop")
    
results
#> # A tibble: 2 x 3
#>   group  year prop_mt_4_provs
#>   <int> <int>           <dbl>
#> 1     0  2020           0.426
#> 2     1  2020           0.423

reprex package (v2.0.0)

创建于 2021-06-22

基于我正在寻找在 90 天内拜访 >=4 位提供者的年度“群体”患者比例,您可以试试这个:

library(data.table) #data.table 1.13.2
setDT(test)[, c("d90ago", "d90aft") := .(DATE - 90L, DATE + 90L)]
setkey(test, PATID, DATE)
test[, grp := 
    .SD[.SD, on=.(PATID, DATE>=d90ago, DATE<=d90aft), by=.EACHI, +(length(unique(x.PROV))>=4L)]$V1
]

以上允许重复使用 90 天重叠 windows 内的 PROV。