我的数据框中的每个观察结果都包含不同的“在日期之前”和“在日期之后的实例”。问题是每个ID的某些日期重叠。例如,在下表中,ID 1和4包含重叠的日期值。
ID before date after date
1 10/1/1996 12/1/1996
1 1/1/1998 9/30/2003
1 1/1/2000 12/31/2004
2 1/1/2001 3/31/2006
3 1/1/2001 9/30/2006
4 1/1/2001 9/30/2005
4 10/1/2004 12/30/2004
4 10/3/2004 11/28/2004
我想尝试这样的事情:
ID before date after date
1 10/1/1996 12/1/1996
1 1/1/1998 12/31/2004
2 1/1/2001 3/31/2006
3 1/1/2001 9/30/2006
4 1/1/2001 9/30/2005
基本上,我想用重叠的值的日期范围替换任何重叠的日期值,单独保留非重叠值,并删除任何不必要的行。不知道怎么做这个
答案 0 :(得分:3)
首先,您应该将字符串日期转换为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
不幸的是,基础R cummax()
函数不适用于Date
- 被分类的对象,所以我不得不写一个cummax.Date()
垫片。我会在帖子结尾处解释ave()
和cummax()
业务的必要性。
如您所见,上述计算通过[-1]
排除第一个元素,滞后于两个矢量化比较中的每一个的RHS。这允许我们将记录的ID
与以下记录的ID
进行比较,并比较其after.date
是否在以下记录的before.date
之后。得到的逻辑向量与ANDed(&
)在一起。然后,该逻辑向量的否定表示不重叠的相邻记录对,因此我们可以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
和before.date
值。这就是我开始使用g[1,]
的原因。然后,我们只需要通过after.date
从群组中获取最好的max(g$after.date)
,然后用after.date
覆盖第一条记录的transform()
。} p>
关于绩效的一个词:关于订购辅助绩效的假设,因为它允许我们通过滞后的矢量化比较简单地将每个记录与紧随其后的记录进行比较,而不是将组中的每个记录与每个其他记录进行比较。
现在,对于ave()
和cummax()
业务。在写完我的答案的初始版本之后,我意识到我的解决方案存在一个缺陷,而这恰好不会被您的示例数据暴露出来。假设一组中有三条记录。如果第一个记录的范围与以下两个记录的两个重叠,然后中间记录不与第三个记录重叠,那么我的(原始)代码将无法确定第三条记录是前两条记录中同一重叠组的一部分。
解决方案是在与以下记录进行比较时不要简单地使用当前记录的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()
计算错误的证明:
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()
调用(如果您计划覆盖原始{{},则更有意义{1}}使用新聚合):
df