我想选择带有日期的数据行,以便日期的最小时差为 3 个月。
下面是一个例子:

    patient numsermed       date
 1:       1   numser1 2020-01-08
 2:       2   numser2 2015-01-02
 3:       2   numser2 2019-12-12
 4:       2   numser2 2020-01-05
 5:       2   numser2 2020-01-08
 6:       2   numser2 2020-01-20
 7:       2   numser2 2020-03-15
 8:       2   numser2 2020-03-18
 9:       2   numser3 2020-03-13
10:       2   numser3 2020-03-18
11:       3   numser3 2020-01-22
12:       4   numser4 2018-01-02
我想通过 patientnumsermed ,保留至少有 3 个月差异的 date 。我不能简单地使用连续的差异。预期结果是:
   patient numsermed       date
1:       1   numser1 2020-01-08
2:       2   numser2 2015-01-02
3:       2   numser2 2019-12-12
4:       2   numser2 2020-03-15
5:       2   numser3 2020-03-13
6:       3   numser3 2020-01-22
7:       4   numser4 2018-01-02
在这里,对于 numsermed2 和患者 2,在 2019-12-12 之后,至少晚 3 个月的下一个日期是 2020-03-15 ,我保留。因此,我删除了 2020-01-052020-01-082020-01-20
然后我删除 2020-03-18 ,这是在 2020-03-15 之后的 3 天。
这是我的 data.table 解决方案:
library(data.table)
library(lubridate)

setkeyv(test,c("numsermed","patient","date"))
test[,indx := .I - .I[1]+1,by = .(numsermed,patient)]

max(test[,.N,by = .(numsermed,patient)]$N)
Nmax <- max(test[,.N,by = .(numsermed,patient)]$N)
test[,supp := 0]

for(i in 1:Nmax){
  test[N>1 ,supp := ifelse(i < indx & date < date[i] + 90,1,0),
       by = .(numsermed,patient)]
  test <- test2[supp != 1  ]
  test[,indx := .I - .I[1]+1,by = .(numsermed,patient)]
}
这个想法是对于每一行,测试条件然后执行子集。它似乎有效,但在百万行表上,它相当慢(几个小时)。我确信在 data.table 中有半等连接或滚动连接的有效方法,但我没有设法编写它。有人可以提出更有效的解决方案吗? dplyr 解决方案当然也受欢迎。
数据:
library(data.table)
library(lubridate)  test<-setDT(list(patient=c(1:3,2),numsermed=c(paste0("numser",1:3),"numser2"),date=as_date(c("2020-01-08","2020-01-20","2020-01-22","2019-12-12"))))
    test<-rbind(test,data.table(patient=4,numsermed="numser4",date=as_date("2018-01-02")))
    test<-rbind(test,data.table(patient=2,numsermed="numser2",date=as_date("2015-01-02")))
    test<-rbind(test,data.table(patient=2,numsermed="numser2",date=as_date("2020-03-15")))
    test<-rbind(test,data.table(patient=2,numsermed="numser2",date=as_date("2020-01-05")))
    test<-rbind(test,data.table(patient=2,numsermed="numser2",date=as_date("2020-01-08")))
    test<-rbind(test,data.table(patient=2,numsermed="numser3",date=as_date("2020-03-13")))
    test<-rbind(test,data.table(patient=2,numsermed="numser3",date=as_date("2020-03-18")))
    test<-rbind(test,data.table(patient=2,numsermed="numser2",date=as_date("2020-03-18")))

编辑
我建议我比较提出的解决方案,@Ben 的解决方案,@chinsoon12 的和 @astrofunkswag 的。
下面是测试数据:
library(data.table)
library(lubridate)
library(magrittr)

set.seed(1234)
origin <- "1970-01-01"
dt <- data.table(numsermed = sample(paste0("numsermed",1:30),10000,replace = T))
dt[,patient := sample(1:10000,.N,replace = T),by = numsermed]
dt[,date := sample((dmy("01.01.2019") %>% as.numeric()):(dmy("01.01.2020") %>% as.numeric()),.N),by = .(patient)]
这里有 4 个功能,包括我的:
ben = function(dt){
  dt[, c("idx", "date2") := list(.I, date - 90L)]
  dt_final <- unique(dt[dt, on = c(patient = "patient", numsermed = "numsermed", date = "date2"),
                            roll = -Inf][order(i.date)], by = "idx")
  setorderv(dt_final, c("patient", "numsermed", "i.date"))
  return(dt_final[,.(patient,numsermed,date = i.date)])
}


chinson = function(dt){
  dt[, d := as.integer(date)]
  setkey(dt,date)
  return( dt[dt[, g := findInterval(d, seq(d[1L], d[.N]+90L, by=90L)), .(patient, numsermed)][,
        .I[1L], .(patient, numsermed, g)]$V1][,.(patient,numsermed,date)])
}

sum_reset_at <- function(thresh) {
  function(x) {
    accumulate(x, ~if_else(.x>=thresh, .y, .x+.y))
  }
}

mon_diff <- function(d1, d2){
  12 * as.numeric((as.yearmon(d1) - as.yearmon(d2)))
}
library(tidyverse); library(zoo)

astrofun = function(dt){
 return(
    dt %>%
     group_by(patient, numsermed) %>%
     mutate(diff1 = mon_diff(date, lag(date)),
            diff1 = if_else(is.na(diff1), 300, diff1)) %>%
     mutate(diff2 = sum_reset_at(3)(diff1)) %>%
     filter(diff2 >= 3) %>%
     select(-contains('diff'))
 )
}

denis = function(dt){
  df <- copy(dt)
  setkeyv(dt,c("numsermed","patient","date"))
  df[,indx := .I - .I[1]+1,by = .(numsermed,patient)]

  df[,N := .N,by = .(numsermed,patient)]
  Nmax <- max(df[,N])
  df[,supp := 0]

  for(i in 1:Nmax){
    df[N>1 ,supp := ifelse(i < indx & date < date[i] + 90,1,0),
         by = .(numsermed,patient)]
    df <- df[supp != 1  ]
    df[,indx := .I - .I[1]+1,by = .(numsermed,patient)]
  }
  return(df[,.(patient,numsermed,date)])
}
首先,它们都不会产生相同的结果! denis(dt) 输出 9833 行,ben(dt) 9928,chinson(dt) 9929,@astrofunkswag 解决方案 astrofun(dt) 输出 9990 行。我不知道为什么这不会产生相同的输出,也不知道什么解决方案是好的(我会说我的只是为了自命不凡,但我什至不确定)。
然后进行基准测试以比较效率。
library(microbenchmark)
microbenchmark(ben(dt),
               chinson(dt),
               astrofun(dt),
               denis(dt),times = 10)


Unit: milliseconds
         expr       min        lq       mean    median        uq       max neval
      ben(dt)   17.3841   19.8321   20.88349   20.9609   21.8815   23.5125    10
  chinson(dt)  230.8868  232.6298  275.16637  236.8482  239.0144  544.2292    10
 astrofun(dt) 4460.2159 4565.9120 4795.98600 4631.3251 5007.8055 5687.7717    10
    denis(dt)   68.0480   68.4170   88.88490   80.9636   90.0514  142.9553    10
@Ben 的滚动连接解决方​​案当然是最快的。我的还不错,@astrofunkswag 的解决方案 super 慢,因为我猜是累积总和。

最佳答案

使用 data.table 您可以尝试以下操作。这将涉及在 90 天前创建第二个日期,然后进行滚动连接。

library(data.table)

setDT(test[, c("idx", "date2") := list(.I, date - 90L)])
test_final <- unique(test[test, on = c(patient = "patient", numsermed = "numsermed", date = "date2"),
                          roll = -Inf][order(i.date)], by = "idx")
setorderv(test_final, c("patient", "numsermed", "i.date"))
test_final

输出

( i.date 具有所需的最终日期)
   patient numsermed       date idx      date2     i.date i.idx
1:       1   numser1 2019-10-10   1 2019-10-10 2020-01-08     1
2:       2   numser2 2014-10-04   6 2014-10-04 2015-01-02     6
3:       2   numser2 2019-09-13   4 2019-09-13 2019-12-12     4
4:       2   numser2 2019-12-16   8 2019-10-07 2020-03-15     7
5:       2   numser3 2019-12-14  10 2019-12-14 2020-03-13    10
6:       3   numser3 2019-10-24   3 2019-10-24 2020-01-22     3
7:       4   numser4 2017-10-04   5 2017-10-04 2018-01-02     5

关于r - 在分组时选择日期之间具有最小时间间隔的行的有效方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60343795/

10-12 17:39
查看更多