R:如何按组扩展条件

R:如何按组扩展条件,r,dataframe,R,Dataframe,我有一个不平衡的小组,对大约80万人进行了700万次观察。我想创建一个等于1的新变量,如果这个人曾经对另一个问题回答过“是”。具体来说,我想创建一个假人,如果这个人曾经吸烟,那么这个假人等于一 假设我的数据集看起来像这样,每个人的ID都是唯一的,有些人被采访过很多次,而其他人只被采访过一次: ID Smoke 1 No 1 No 1 Yes 1 No 2 No 2 No 3 Yes 3 No

我有一个不平衡的小组,对大约80万人进行了700万次观察。我想创建一个等于1的新变量,如果这个人曾经对另一个问题回答过“是”。具体来说,我想创建一个假人,如果这个人曾经吸烟,那么这个假人等于一

假设我的数据集看起来像这样,每个人的ID都是唯一的,有些人被采访过很多次,而其他人只被采访过一次:

ID   Smoke 
 1      No  
 1      No
 1     Yes
 1      No
 2      No
 2      No
 3     Yes
 3      No
我想生成一个变量,比如:

ID   Smoke  Ever_Smoked
 1      No            1
 1      No            1
 1     Yes            1
 1      No            1
 2      No            0
 2      No            0
 3     Yes            1
 3      No            1
有什么办法吗?
提前谢谢

您可以沿ID分割数据,并分别为每个子集分配适当的值:

d <- data.frame(ID = c(rep(1,4), rep(2,2), rep(3,2)), Smoke=c('No', 'No', 'Yes', rep('No', 3), 'Yes', 'No'))

library(plyr)
d2 <- ldply(split(d, d$ID), function(d_tmp) {
    d_tmp$Ever_Smoked <- ifelse(all(d_tmp$Smoke=='No'), 0, 1)
    d_tmp
})[,-1]

考虑到数据集的大小,基于data.table的解决方案可能是最好/最快的选择

library(data.table)

setDT(df)[, Ever_Smoked := as.numeric(any(Smoke=="Yes")), by = ID]
使用@bgoldst提供的样本数据进行性能测试:

df <- data.frame(ID=c(1L,1L,1L,1L,2L,2L,3L,3L),Smoke=c('No','No','Yes','No','No','No','Yes','No'),stringsAsFactors=F)

# make it a 8 million row dataset 
df <- df[rep(seq_len(nrow(df)), 1000000), ] 

system.time( setDT(df)[, Ever_Smoked := as.numeric(any(Smoke=="Yes")), by = ID] )

#>  user  system elapsed 
#>  0.27    0.01    0.32 

下面是一个使用ave的基本R解决方案:

资料

巧合的是,发现一元加号是最快的。这就是为什么我在这里选择它作为我的答案


当然,对ave的调用将导致严重的性能损失,至少相对于data.table的索引实现而言是如此。因此,为了获得最佳性能,我建议您使用rafa的data.table实现,但使用一元加一元解决方案将逻辑转换为整数。

@Angustin Indaco您可以使用4个空格作为缩进的方式格式化表格,这使它们成为源代码。谢谢,我正在尝试解决如何做到这一点。我对StackOverflow很感兴趣,这是我第一次发表文章。我同意这一点——我想我应该建议更多的数据表解决方案;然而,这并不能给出正确的结果,因为并非所有曾经吸烟的病例最终都是该群体的一例。你需要像setDTdf[,Ever_Smoked:=as.numericanySmoke==Yes,by=ID]这样的东西来代替。最近的邮件很好,谢谢你的提醒!是的,这很快。就我所知,它似乎工作得很好。谢谢
df <- data.frame(ID=c(1L,1L,1L,1L,2L,2L,3L,3L),Smoke=c('No','No','Yes','No','No','No','Yes','No'),stringsAsFactors=F)

# make it a 8 million row dataset 
df <- df[rep(seq_len(nrow(df)), 1000000), ] 

system.time( setDT(df)[, Ever_Smoked := as.numeric(any(Smoke=="Yes")), by = ID] )

#>  user  system elapsed 
#>  0.27    0.01    0.32 
df$Ever_Smoked <- ave(+(df$Smoke=='Yes'),df$ID,FUN=max);
df;
##   ID Smoke Ever_Smoked
## 1  1    No           1
## 2  1    No           1
## 3  1   Yes           1
## 4  1    No           1
## 5  2    No           0
## 6  2    No           0
## 7  3   Yes           1
## 8  3    No           1
df <- data.frame(ID=c(1L,1L,1L,1L,2L,2L,3L,3L),Smoke=c('No','No','Yes','No','No','No','Yes',
'No'),stringsAsFactors=F);