首先,您应该将字符串日期转换为Date
- 分类值,这将使比较成为可能。以下是我定义和强制您的数据的方式:
df <- data.frame(ID=c(1,1,1,2,3,4,4,4), before.date=c('10/1/1996','1/1/1998','1/1/2000','1/1/2001','1/1/2001','1/1/2001','10/1/2004','10/3/2004'), after.date=c('12/1/1996','9/30/2003','12/31/2004','3/31/2006','9/30/2006','9/30/2005','12/30/2004','11/28/2004') );
dcis <- grep('date$',names(df));
df[dcis] <- lapply(df[dcis],as.Date,'%m/%d/%Y');
df;
## ID before.date after.date
## 1 1 1996-10-01 1996-12-01
## 2 1 1998-01-01 2003-09-30
## 3 1 2000-01-01 2004-12-31
## 4 2 2001-01-01 2006-03-31
## 5 3 2001-01-01 2006-09-30
## 6 4 2001-01-01 2005-09-30
## 7 4 2004-10-01 2004-12-30
## 8 4 2004-10-03 2004-11-28
现在,我的解决方案涉及计算一个“重叠分组”向量,我称之为og
。它假设输入df
是由ID
进而before.date
,它位于您的示例数据中。如果没有,这可以通过以下方式实现df[order(df$ID,df$before.date),]
。这是我的计算方法og
:
cummax.Date <- function(x) as.Date(cummax(as.integer(x)),'1970-01-01');
og <- with(df,c(0,cumsum(!(ID[-length(ID)]==ID[-1] & ave(after.date,ID,FUN=cummax)[-length(after.date)]>before.date[-1]))));
og;
## [1] 0 1 1 2 3 4 4 4
不幸的是,基础Rcummax()
功能不起作用Date
- 分类对象,所以我必须写一个cummax.Date()
垫片。我将解释一下需要ave()
and cummax()
业务在帖子末尾。
正如您所看到的,通过排除第一个元素,上述计算滞后于两个向量化比较中每一个的 RHS[-1]
。这使我们能够比较记录的ID
与以下记录相等ID
,并比较是否是after.date
是在之后before.date
以下记录。得到的逻辑向量进行 AND 运算 (&
) 一起。然后,该逻辑向量的否定表示相邻的记录对not重叠,因此我们可以cumsum()
结果(并在前面添加零,因为第一个记录必须以零开头)以获得我们的分组向量。
最后,对于解决方案的最后一部分,我使用了by()
独立与每个重叠组合作:
do.call(rbind,by(df,og,function(g) transform(g[1,],after.date=max(g$after.date))));
## ID before.date after.date
## 0 1 1996-10-01 1996-12-01
## 1 1 1998-01-01 2004-12-31
## 2 2 2001-01-01 2006-03-31
## 3 3 2001-01-01 2006-09-30
## 4 4 2001-01-01 2005-09-30
由于组中的所有记录必须具有相同的ID
,并且我们假设记录按以下顺序排序before.date
(在被订购后ID
,不再相关),我们可以得到正确的ID
and before.date
组中第一条记录的值。这就是为什么我开始g[1,]
。那么我们只需要得到最大的after.date
从小组通过max(g$after.date)
,并覆盖第一条记录after.date
与此,我已经完成了transform()
.
关于性能的一句话:关于排序的假设有助于提高性能,因为它允许我们通过滞后矢量化比较简单地将每个记录与紧随其后的记录进行比较,而不是将组中的每个记录与其他记录进行比较。
现在,对于ave()
and cummax()
商业。在写完答案的初始版本后,我意识到我的解决方案存在缺陷,而您的示例数据恰好没有暴露该缺陷。假设一组中有 3 个记录。如果第一条记录的范围与both下面两条记录,然后中间的记录not与第三条记录重叠,那么我的(原始)代码将无法识别第三条记录是前两条记录的同一重叠组的一部分。
解决方案不是简单地使用after.date
与后续记录进行比较时使用当前记录,而是使用累积最大值after.date
组内。如果任何较早的记录完全超出了紧随其后的记录,那么它显然与该记录重叠,并且它的after.date
在考虑后续记录的重叠组时重要的是。
这是需要此修复的输入数据的演示,使用您的df
作为基础:
df2 <- df;
df2[7,'after.date'] <- '2004-10-02';
df2;
## ID before.date after.date
## 1 1 1996-10-01 1996-12-01
## 2 1 1998-01-01 2003-09-30
## 3 1 2000-01-01 2004-12-31
## 4 2 2001-01-01 2006-03-31
## 5 3 2001-01-01 2006-09-30
## 6 4 2001-01-01 2005-09-30
## 7 4 2004-10-01 2004-10-02
## 8 4 2004-10-03 2004-11-28
现在记录 6 与记录 7 和 8 都重叠,但记录 7 不与记录 8 重叠。该解决方案仍然有效:
cummax.Date <- function(x) as.Date(cummax(as.integer(x)),'1970-01-01');
og <- with(df2,c(0,cumsum(!(ID[-length(ID)]==ID[-1] & ave(after.date,ID,FUN=cummax)[-length(after.date)]>before.date[-1]))));
og;
## [1] 0 1 1 2 3 4 4 4
do.call(rbind,by(df2,og,function(g) transform(g[1,],after.date=max(g$after.date))));
## ID before.date after.date
## 0 1 1996-10-01 1996-12-01
## 1 1 1998-01-01 2004-12-31
## 2 2 2001-01-01 2006-03-31
## 3 3 2001-01-01 2006-09-30
## 4 4 2001-01-01 2005-09-30
这是一个证明og
如果没有ave()
/cummax()
fix:
og <- with(df2,c(0,cumsum(!(ID[-length(ID)]==ID[-1] & after.date[-length(after.date)]>before.date[-1]))));
og;
## [1] 0 1 1 2 3 4 4 5
对解决方案进行小调整,覆盖after.date
提前og
计算,并避免max()
调用(如果您打算覆盖原始内容,则更有意义df
使用新的聚合):
cummax.Date <- function(x) as.Date(cummax(as.integer(x)),'1970-01-01');
df$after.date <- ave(df$after.date,df$ID,FUN=cummax);
df;
## ID before.date after.date
## 1 1 1996-10-01 1996-12-01
## 2 1 1998-01-01 2003-09-30
## 3 1 2000-01-01 2004-12-31
## 4 2 2001-01-01 2006-03-31
## 5 3 2001-01-01 2006-09-30
## 6 4 2001-01-01 2005-09-30
## 7 4 2004-10-01 2005-09-30
## 8 4 2004-10-03 2005-09-30
og <- with(df,c(0,cumsum(!(ID[-length(ID)]==ID[-1] & after.date[-length(after.date)]>before.date[-1]))));
og;
## [1] 0 1 1 2 3 4 4 4
df <- do.call(rbind,by(df,og,function(g) transform(g[1,],after.date=g$after.date[nrow(g)])));
df;
## ID before.date after.date
## 0 1 1996-10-01 1996-12-01
## 1 1 1998-01-01 2004-12-31
## 2 2 2001-01-01 2006-03-31
## 3 3 2001-01-01 2006-09-30
## 4 4 2001-01-01 2005-09-30