【问题标题】:Aggregate dataframe by user, keeping rows for each user prior to first occurrence of treatment按用户聚合数据框,在第一次发生治疗之前为每个用户保留行
【发布时间】:2024-01-17 08:59:01
【问题描述】:

在本网站的其他地方也有类似的问题,但没有一个答案包含我需要做的所有事情。

我有一个数据框,我正试图将其更改为时变数据。研究中的受试者可以从不治疗变为治疗,但不能反过来。受试者有多行治疗信息,我想找到第一次出现的治疗,这很简单。问题是并不是每个人都经历过这种治疗,因此每当我运行我的算法来寻找第一次发生的时候,这些人都会被删除。为了让我的问题更清楚:

ID    treatment    start.date    stop.date  
1        0         01/01/2002    01/02/2002  
1        0         01/02/2002    01/03/2002  
1        1         01/03/2002    01/04/2002  
1        0         01/04/2002    01/05/2002  
2        0         01/01/2002    01/02/2002  
2        0         01/02/2002    01/03/2002  
3        0         01/01/2002    01/02/2002  
3        1         01/02/2002    01/03/2002
3        0         01/03/2002    01/04/2002  

如您所见,2 从未接受过治疗。当我运行以下算法时,2 被删除。

data$keep <- with(data, 
                     ave(treatment==1, ID ,FUN=function(x) if(1 %in% x) cumsum(x) else 2))
with(data, data[keep==0 | (treatment==1 & keep==1),]) 

有什么方法可以扩展此代码,以便它保留那些没有第一次出现的人并且保持每一行直到第一次出现的人有它?

总而言之,我希望我的数据如下所示:

ID    treatment    start.date    stop.date    
1        0         01/01/2002    01/02/2002   
1        0         01/02/2002    01/03/2002    
1        1         01/03/2002    01/04/2002   
2        0         01/01/2002    01/02/2002    
2        0         01/02/2002    01/03/2002  
3        0         01/01/2002    01/02/2002  
3        1         01/02/2002    01/03/2002

【问题讨论】:

  • 鉴于您正在进行聚合,您真的应该学习使用dplyrdata.table 进行拆分-应用-组合。任何更少的东西都会很快耗尽,并且代码几乎是只写的;非常难以重用或理解。

标签: r aggregate find-occurrences


【解决方案1】:

我们可以通过不同的方式做到这一点。 data.table 的一个选项是在按“ID”列分组的“治疗”列上使用if/else 条件。我们检查if处理中没有值等于'1',然后返回Data.table的子集(.SD)即(if(!any(treatment==1)) .SD)或else,即如果'1'值在'treatment' 返回处理中等于 1 的第一个值的位置索引 (which(treatment==1)[1L]),获取序列 (seq) 并使用该数字索引对数据表进行子集化。 (.SD)

library(data.table)#v1.9.5+
setDT(data)[, if(!any(treatment==1)) .SD 
              else .SD[seq(which(treatment==1)[1L])], by = ID]
#     ID treatment start.date  stop.date
#1:  1         0 01/01/2002 01/02/2002
#2:  1         0 01/02/2002 01/03/2002
#3:  1         1 01/03/2002 01/04/2002
#4:  2         0 01/01/2002 01/02/2002
#5:  2         0 01/02/2002 01/03/2002
#6:  3         0 01/01/2002 01/02/2002
#7:  3         1 01/02/2002 01/03/2002

或者更简洁的方法是依靠“处理”中当前值和先前值之间的差异,并检查差异是否大于或等于 0。我们可以使用diff-。在这种情况下,我得到了治疗和治疗滞后之间的差异(shift 默认给出“滞后”值。它是 data.table 的开发版本中的一个新功能)

setDT(data)[, .SD[(treatment-shift(treatment, fill=0))>=0], by = ID]

或使用dplyr 的类似方法。我们按“ID”分组,然后 filter 根据“治疗”中当前值与先前值之间的差异对行进行分组。

library(dplyr)
data %>% 
    group_by(ID) %>% 
    filter(c(0, diff(treatment)) >=0) 
#  ID treatment start.date  stop.date
#1  1         0 01/01/2002 01/02/2002
#2  1         0 01/02/2002 01/03/2002
#3  1         1 01/03/2002 01/04/2002
#4  2         0 01/01/2002 01/02/2002
#5  2         0 01/02/2002 01/03/2002
#6  3         0 01/01/2002 01/02/2002
#7  3         1 01/02/2002 01/03/2002

或者用ave 来自base R

data[with(data, as.logical(ave(treatment, ID, 
                  FUN=function(x) c(0, diff(x))>=0))),]

【讨论】:

  • 出于好奇,.SD 是做什么的?
  • @Lb93 .SD 表示Subset of Datatable。正如它所暗示的,它根据提供的条件获取数据集行/列的子集。
  • @Lb93,请查看data.table vignette 简介 here.. 大约需要 10 分钟才能完成.. 应该能让您跟上进度。