根据时间窗口进行重复数据删除

时间:2016-11-03 10:03:19

标签: r duplicates

我有大量个人的数据,每个人可能有多个观察结果。我希望将数据重复数据删除为每个人28天的“剧集”。我想删除那些观察日期是28天或小于前一集开始日期的记录。

以下是对单个人的6次观察的一些样本数据。 duplicatenew_episode变量是虚拟变量,不存在于原始数据中,表示该示例的逻辑。

dat <- data.frame(id = rep(1, 6), spec_n = seq(1,6,1), 
                  spec_date = as.Date(c("2016/01/01", "2016/01/02", "2016/01/30",
                                        "2016/01/31", "2016/02/02", "2016/02/28")),
                  duplicate = c(0,1,0,1,1,0), new_episode = c(1,0,1,0,0,1),
                   stringsAsFactors = FALSE)
dat
  id spec_n  spec_date duplicate new_episode
1  1      1 2016-01-01         0           1
2  1      2 2016-01-02         1           0
3  1      3 2016-01-30         0           1
4  1      4 2016-01-31         1           0
5  1      5 2016-02-02         1           0
6  1      6 2016-02-28         0           1

使用dplyr,我可以计算自上次观察以来的时间和自第一集以来的时间。因此,date_diff上的重复数据删除不会提供我需要的数据。

library(dplyr)
dat <- dat %>% group_by(id) %>% 
  mutate(date_diff = spec_date - lag(spec_date),
         earliest_spec_date = min(spec_date), 
         diff_earliest = spec_date - earliest_spec_date)
dat
     id spec_n  spec_date duplicate new_episode date_diff earliest_spec_date diff_earliest
  <dbl>  <dbl>     <date>     <dbl>       <dbl>    <time>             <date>        <time>
1     1      1 2016-01-01         0           1   NA days         2016-01-01        0 days
2     1      2 2016-01-02         1           0    1 days         2016-01-01        1 days
3     1      3 2016-01-30         0           1   28 days         2016-01-01       29 days
4     1      4 2016-01-31         1           0    1 days         2016-01-01       30 days
5     1      5 2016-02-02         1           0    2 days         2016-01-01       32 days
6     1      6 2016-02-28         0           1   26 days         2016-01-01       58 days

然而,这并不能完全满足我的需要。 spec_n == 6自上一次观察以来不到28天,超过最后一集开始后的28天(spec_n == 3)。

预期输出将是重复为0或new_episode为1的行,例如

     id spec_n  spec_date duplicate new_episode date_diff earliest_spec_date diff_earliest
  <dbl>  <dbl>     <date>     <dbl>       <dbl>    <time>             <date>        <time>
1     1      1 2016-01-01         0           1   NA days         2016-01-01        0 days
2     1      3 2016-01-30         0           1   28 days         2016-01-01       29 days
3     1      6 2016-02-28         0           1   26 days         2016-01-01       58 days

1 个答案:

答案 0 :(得分:1)

这应该有用(它是我认为Llopis建议的实现)。

我先制作一些模拟数据:

df <- data.frame(date = seq(as.Date("2015-01-01"), as.Date("2015-12-31"), by=1), data=rnorm(365))
head(df)
        date       data
1 2015-01-01 -1.4493544
2 2015-01-02 -0.8860342
3 2015-01-03  1.3629541
4 2015-01-04 -2.0131108
5 2015-01-05 -0.4527413
6 2015-01-06  0.8428585

现在我们编写一个函数,它接受第一个日期并检查后续日期是否超过28天,如果不是则返回0,如果不是则返回1。如果日期是28天,则需要将新日期作为未来比较的基础。

dupFinder <- function(x) {
  n <- 1
  N <- length(x)
  res <- rep(1, N)
  start <- x[n]
  while (n < (N)) {
    if (as.numeric(x[n+1]-start)>=28) {
      res[n+1] <- 1
      n <- n+1
      start <- x[n]
    }
    else {
      res[n+1] <- 0
      n <- n+1
    }
  }
  return(res)
}

函数dupFinder将返回一个长度等于数据帧的向量,然后您可以使用它将数据帧子集化为感兴趣的行。因此:

df[dupFinder(df$date)==1,]
          date       data
1   2015-01-01 -1.4493544
29  2015-01-29  0.2084123
57  2015-02-26  1.4541566
85  2015-03-26  0.6794230
113 2015-04-23 -0.8285670
141 2015-05-21 -0.8686872
169 2015-06-18  2.1657994
197 2015-07-16 -1.1802231
225 2015-08-13  0.1808395
253 2015-09-10 -0.4762835
281 2015-10-08 -0.3769593
309 2015-11-05  0.2825544
337 2015-12-03 -0.7132649
365 2015-12-31 -1.8111226

正如预期的那样,我们从1月1日开始,然后是1月29日,然后是2月26日,因为2月有28天我们接下来要到3月26日等等。